From 6608f8fc19ef4b587596c9ed3cb3b3fcc37c1eb6 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 19 Sep 2015 20:47:37 +0200 Subject: [PATCH 001/201] Startup webkit2 branch from the scratch. --- .gitignore | 10 +- CHANGELOG.md | 8 + Makefile | 54 +- README.md | 200 +- config.mk | 88 +- doc/vimb.1 | 1617 ------------ examples/formfiller/formfiller | 78 - examples/formfiller/scripts.js | 50 - src/.gitignore | 2 + src/Makefile | 58 +- src/arh.c | 199 -- src/ascii.h | 7 +- src/autocmd.c | 488 ---- src/autocmd.h | 50 - src/bookmark.c | 326 --- src/bookmark.h | 34 - src/command.c | 180 +- src/command.h | 11 +- src/completion.c | 217 +- src/completion.h | 16 +- src/config.def.h | 102 +- src/cookiejar.c | 105 - src/cookiejar.h | 43 - src/dom.c | 330 --- src/dom.h | 46 - src/ex.c | 632 ++--- src/ex.h | 11 +- src/ext-proxy.c | 214 ++ src/{arh.h => ext-proxy.h} | 17 +- src/handlers.c | 96 - src/hints.c | 398 --- src/hints.js | 611 ----- src/hints.js.h | 1 + src/history.c | 172 +- src/history.h | 6 +- src/hsts.c | 458 ---- src/hsts.h | 52 - src/input.c | 138 +- src/input.h | 8 +- src/io.c | 170 -- src/js.c | 179 +- src/js.h | 14 +- src/js2h.sh | 15 - src/main.c | 2342 ++++++----------- src/main.h | 477 ++-- src/map.c | 610 ++--- src/map.h | 14 +- src/normal.c | 489 ++-- src/normal.h | 12 +- src/scripts/.gitignore | 1 + src/scripts/Makefile | 19 + src/scripts/increment_uri_number.js | 16 + src/scripts/js2h.sh | 38 + src/setting.c | 793 ++---- src/setting.h | 10 +- src/shortcut.c | 56 +- src/shortcut.h | 15 +- src/util.c | 758 ++---- src/util.h | 35 +- src/webextension/Makefile | 19 + src/webextension/ext-dom.c | 179 ++ src/{handlers.h => webextension/ext-dom.h} | 18 +- src/webextension/ext-main.c | 360 +++ src/{hints.h => webextension/ext-main.h} | 20 +- src/webextension/ext-util.c | 60 + src/{io.h => webextension/ext-util.h} | 13 +- tests/Makefile | 19 - tests/manual/112-editable-focus.html | 39 - tests/manual/146-hsts-iframe.html | 14 - .../manual/201-editable-focus-in-iframes.html | 9 - tests/test-handlers.c | 80 - tests/test-map.c | 176 -- tests/test-shortcut.c | 142 - tests/test-util.c | 285 -- 74 files changed, 3766 insertions(+), 10863 deletions(-) delete mode 100644 doc/vimb.1 delete mode 100755 examples/formfiller/formfiller delete mode 100644 examples/formfiller/scripts.js create mode 100644 src/.gitignore delete mode 100644 src/arh.c delete mode 100644 src/autocmd.c delete mode 100644 src/autocmd.h delete mode 100644 src/bookmark.c delete mode 100644 src/bookmark.h delete mode 100644 src/cookiejar.c delete mode 100644 src/cookiejar.h delete mode 100644 src/dom.c delete mode 100644 src/dom.h create mode 100644 src/ext-proxy.c rename src/{arh.h => ext-proxy.h} (72%) delete mode 100644 src/handlers.c delete mode 100644 src/hints.c delete mode 100644 src/hints.js create mode 100644 src/hints.js.h delete mode 100644 src/hsts.c delete mode 100644 src/hsts.h delete mode 100644 src/io.c delete mode 100755 src/js2h.sh create mode 100644 src/scripts/.gitignore create mode 100644 src/scripts/Makefile create mode 100644 src/scripts/increment_uri_number.js create mode 100755 src/scripts/js2h.sh create mode 100644 src/webextension/Makefile create mode 100644 src/webextension/ext-dom.c rename src/{handlers.h => webextension/ext-dom.h} (67%) create mode 100644 src/webextension/ext-main.c rename src/{hints.h => webextension/ext-main.h} (60%) create mode 100644 src/webextension/ext-util.c rename src/{io.h => webextension/ext-util.h} (80%) delete mode 100644 tests/Makefile delete mode 100644 tests/manual/112-editable-focus.html delete mode 100644 tests/manual/146-hsts-iframe.html delete mode 100644 tests/manual/201-editable-focus-in-iframes.html delete mode 100644 tests/test-handlers.c delete mode 100644 tests/test-map.c delete mode 100644 tests/test-shortcut.c delete mode 100644 tests/test-util.c diff --git a/.gitignore b/.gitignore index 00a41de6..711ddb0e 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 77da6f65..9fb2ea46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index e21743ec..b7abcfd9 100644 --- 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 diff --git a/README.md b/README.md index 09441004..b2e03318 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,111 @@ -# 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. diff --git a/config.mk b/config.mk index 38093361..e3f18005 100644 --- 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 index 7c0e2ea8..00000000 --- a/doc/vimb.1 +++ /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" | 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 `` and `` 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 , or , , -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 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\fP -Selects the first highlighted element, or the current focused. -.IP "\fB\fP" -Moves the focus to the next hint element. -.IP "\fB\fP" -Moves the focus to the previous hint element. -.IP "\fB, 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 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 , CTRL\-[, CTRL-C -Ignore all typed content and switch back to normal mode. -.TP -.B -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 -Start completion of the content in the inputbox in forward direction. -.TP -.B -Start completion of the content in the inputbox in backward direction. -.TP -.B -Step backward in the command history. -.TP -.B -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 "". -.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: , , , -for the cursor keys, , , , , , - and -. -.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 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 :set scripts=on:open !glib" -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://:/ 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 , 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 `` or `` 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" -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 . -.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 tag. -Keep in mind that Java content can be still shown in the page -through or , 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 % ;" -This fills the inputbox with the prefilled download command and replaces -`%' with the current URI and `;' 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 -`URItitlespace separated tags' or as -.br -`URItags` 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 index 05edd497..00000000 --- a/examples/formfiller/formfiller +++ /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 "::e! _vbform.fill($data);" | 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 index 83574093..00000000 --- a/examples/formfiller/scripts.js +++ /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 index 00000000..ca46d8a5 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +config.h +vimb diff --git a/src/Makefile b/src/Makefile index b72d7c9e..e4e2faea 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 index 6fd0f8ed..00000000 --- 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/ascii.h b/src/ascii.h index a5234e1c..f375a302 100644 --- a/src/ascii.h +++ b/src/ascii.h @@ -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 index e856a4c1..00000000 --- a/src/autocmd.c +++ /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 index aef09a2d..00000000 --- a/src/autocmd.h +++ /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 index adc2e032..00000000 --- a/src/bookmark.c +++ /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 titletags */ - 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 index 32aaba47..00000000 --- a/src/bookmark.h +++ /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 */ diff --git a/src/command.c b/src/command.c index a5bb9f2f..c957c463 100644 --- a/src/command.c +++ b/src/command.c @@ -21,128 +21,86 @@ * 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 -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 diff --git a/src/command.h b/src/command.h index 68c8d85a..92cf4941 100644 --- a/src/command.h +++ b/src/command.h @@ -20,6 +20,9 @@ #ifndef _COMMAND_H #define _COMMAND_H +#include +#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 */ diff --git a/src/completion.c b/src/completion.c index a15b081b..8bfb9b03 100644 --- a/src/completion.c +++ b/src/completion.c @@ -17,25 +17,54 @@ * 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 + * or from selected item on. */ } - return true; + return TRUE; } diff --git a/src/completion.h b/src/completion.h index 26b057df..e353ecba 100644 --- a/src/completion.h +++ b/src/completion.h @@ -20,8 +20,12 @@ #ifndef _COMPLETION_H #define _COMPLETION_H +#include + #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 */ diff --git a/src/config.def.h b/src/config.def.h index 97531bf5..da33cc76 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -17,101 +17,35 @@ * 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 index a16392f4..00000000 --- a/src/cookiejar.c +++ /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 -#include -#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 index bca9758a..00000000 --- a/src/cookiejar.h +++ /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 index 32b6bea8..00000000 --- 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 index 03eaf37b..00000000 --- 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 - -/* 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 */ diff --git a/src/ex.c b/src/ex.c index bfbe418d..914a5549 100644 --- a/src/ex.c +++ b/src/ex.c @@ -21,32 +21,23 @@ * This file contains function to handle input editing, parsing of called ex * commands from inputbox and the ex commands. */ -#include "config.h" + +#include #include -#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 or 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) diff --git a/src/ex.h b/src/ex.h index cbd12819..1173d90a 100644 --- a/src/ex.h +++ b/src/ex.h @@ -23,12 +23,11 @@ #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 index 00000000..75ccbd77 --- /dev/null +++ b/src/ext-proxy.c @@ -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 +#include + +#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/arh.h b/src/ext-proxy.h similarity index 72% rename from src/arh.h rename to src/ext-proxy.h index d235b363..b5b33a16 100644 --- a/src/arh.h +++ b/src/ext-proxy.h @@ -2,7 +2,6 @@ * 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 @@ -18,17 +17,13 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" -#ifdef FEATURE_ARH - -#ifndef _ARH_H -#define _ARH_H +#ifndef _EXT_PROXY_H +#define _EXT_PROXY_H #include "main.h" -GSList *arh_parse(const char *, const char **); -void arh_free(GSList *); -void arh_run(GSList *, const char *, SoupMessage *); +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: _ARH_H */ -#endif +#endif /* end of include guard: _EXT_PROXY_H */ diff --git a/src/handlers.c b/src/handlers.c deleted file mode 100644 index 66625bf6..00000000 --- a/src/handlers.c +++ /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/hints.c b/src/hints.c deleted file mode 100644 index 91bb869e..00000000 --- a/src/hints.c +++ /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 -#include -#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.js b/src/hints.js deleted file mode 100644 index 458d85c8..00000000 --- a/src/hints.js +++ /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 index 00000000..102d9c6b --- /dev/null +++ b/src/hints.js.h @@ -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;ioffsets.bottom||rect.bottomoffsets.right||rect.right=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;i0);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=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=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,};})());" diff --git a/src/history.c b/src/history.c index ceeb5e0f..fcd89fce 100644 --- a/src/history.c +++ b/src/history.c @@ -17,78 +17,79 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" #include +#include #include -#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); -} diff --git a/src/history.h b/src/history.h index a6862857..8709f5d6 100644 --- a/src/history.h +++ b/src/history.h @@ -20,6 +20,10 @@ #ifndef _HISTORY_H #define _HISTORY_H +#include + +#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 index 00b25a13..00000000 --- a/src/hsts.c +++ /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 -#include -#include -#include -#include - -#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 index c56b2c79..00000000 --- a/src/hsts.h +++ /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 -#include - -#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 diff --git a/src/input.c b/src/input.c index 5292b4e7..b5381ffd 100644 --- a/src/input.c +++ b/src/input.c @@ -17,62 +17,57 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" +#include #include -#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); -} diff --git a/src/input.h b/src/input.h index 8887abca..64d1d5c1 100644 --- a/src/input.h +++ b/src/input.h @@ -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 index 0abac30c..00000000 --- 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 -#include -#include -#include -#include -#include - -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/js.c b/src/js.c index fb402365..e6f467ef 100644 --- a/src/js.c +++ b/src/js.c @@ -17,175 +17,36 @@ * 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 +#include +#include - 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; -} diff --git a/src/js.h b/src/js.h index 5081bf9e..1a63d0a3 100644 --- a/src/js.h +++ b/src/js.h @@ -20,16 +20,8 @@ #ifndef _JS_H #define _JS_H -#include "main.h" +#include -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 index f849e858..00000000 --- a/src/js2h.sh +++ /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 "" diff --git a/src/main.c b/src/main.c index 3b379fc8..a8c9f74d 100644 --- a/src/main.c +++ b/src/main.c @@ -17,138 +17,76 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" -#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include "main.h" -#include "util.h" -#include "command.h" -#include "setting.h" +#include + +#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 \n"); + fprintf(stdout, "License GPLv3+: GNU GPL version 3 or later \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; } diff --git a/src/main.h b/src/main.h index 1bde3cd2..f0816d04 100644 --- a/src/main.h +++ b/src/main.h @@ -20,31 +20,24 @@ #ifndef _MAIN_H #define _MAIN_H -#include -#include -#include -#include -#include #include -#include -#ifdef HAS_GTK3 -#include #include -#endif -#include "config.h" -#ifdef FEATURE_HSTS -#include "hsts.h" -#endif +#include +#include -/* 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);} @@ -58,174 +51,88 @@ #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 */ 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 */ diff --git a/src/map.c b/src/map.c index a9fad553..5b4017a9 100644 --- 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 @@ -15,47 +17,37 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include #include +#include +#include + +#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 { {"", 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 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 and * 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 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 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 -> \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 */ + 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 */ - 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 -> \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); -} diff --git a/src/map.h b/src/map.h index 03366661..4d5308ad 100644 --- 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 */ diff --git a/src/normal.c b/src/normal.c index 54548a14..2980ecf5 100644 --- a/src/normal.c +++ b/src/normal.c @@ -18,14 +18,15 @@ */ #include +#include + +#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 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; diff --git a/src/normal.h b/src/normal.h index 710a5535..e4f626c8 100644 --- a/src/normal.h +++ b/src/normal.h @@ -23,11 +23,11 @@ #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 index 00000000..763376e4 --- /dev/null +++ b/src/scripts/.gitignore @@ -0,0 +1 @@ +scripts.h diff --git a/src/scripts/Makefile b/src/scripts/Makefile new file mode 100644 index 00000000..32af1912 --- /dev/null +++ b/src/scripts/Makefile @@ -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 index 00000000..d90adfb9 --- /dev/null +++ b/src/scripts/increment_uri_number.js @@ -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 index 00000000..b1770851 --- /dev/null +++ b/src/scripts/js2h.sh @@ -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/' diff --git a/src/setting.c b/src/setting.c index 9c0492ff..79d3ebb3 100644 --- a/src/setting.c +++ b/src/setting.c @@ -17,27 +17,15 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" +#include #include + +#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 ;", 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 @@ static int setting_set_value(Setting *prop, void *value, SettingType type) /** * 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; } diff --git a/src/setting.h b/src/setting.h index 96319207..cd1d8d50 100644 --- a/src/setting.h +++ b/src/setting.h @@ -20,11 +20,13 @@ #ifndef _SETTING_H #define _SETTING_H +#include + #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 */ diff --git a/src/shortcut.c b/src/shortcut.c index 37b083f7..0663b9f8 100644 --- a/src/shortcut.c +++ b/src/shortcut.c @@ -17,65 +17,66 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ +#include +#include + +#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; } diff --git a/src/shortcut.h b/src/shortcut.h index c521984f..f932abf1 100644 --- a/src/shortcut.h +++ b/src/shortcut.h @@ -20,12 +20,13 @@ #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 */ + diff --git a/src/util.c b/src/util.c index 42075987..9670b848 100644 --- a/src/util.c +++ b/src/util.c @@ -17,108 +17,155 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" +#include #include -#include -#include +#include #include -#include -#include "main.h" -#include "util.h" +#include +#include +#include + #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; } diff --git a/src/util.h b/src/util.h index 9ed1236b..c09b0987 100644 --- a/src/util.h +++ b/src/util.h @@ -20,6 +20,7 @@ #ifndef _UTIL_H #define _UTIL_H +#include #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 index 00000000..671c23f0 --- /dev/null +++ b/src/webextension/Makefile @@ -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 index 00000000..26aeaf0f --- /dev/null +++ b/src/webextension/ext-dom.c @@ -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 +#include + +#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/handlers.h b/src/webextension/ext-dom.h similarity index 67% rename from src/handlers.h rename to src/webextension/ext-dom.h index a38d9e1b..657821d0 100644 --- a/src/handlers.h +++ b/src/webextension/ext-dom.h @@ -17,14 +17,14 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#ifndef _HANDLERS_H -#define _HANDLERS_H +#ifndef _EXT_DOM_H +#define _EXT_DOM_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); +#include +#include -#endif /* end of include guard: _HANDLERS_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 index 00000000..669fd7db --- /dev/null +++ b/src/webextension/ext-main.c @@ -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 +#include +#include +#include +#include + +#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[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +/* 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/hints.h b/src/webextension/ext-main.h similarity index 60% rename from src/hints.h rename to src/webextension/ext-main.h index 4ced9d89..61788004 100644 --- a/src/hints.h +++ b/src/webextension/ext-main.h @@ -17,19 +17,11 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#ifndef _HINTS_H -#define _HINTS_H +#ifndef _EXT_MAIN_H +#define _EXT_MAIN_H -#include "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" -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 */ +#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 index 00000000..0bb40e41 --- /dev/null +++ b/src/webextension/ext-util.c @@ -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 +#include +#include +#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/io.h b/src/webextension/ext-util.h similarity index 80% rename from src/io.h rename to src/webextension/ext-util.h index 7fa894b1..452c4276 100644 --- a/src/io.h +++ b/src/webextension/ext-util.h @@ -17,16 +17,11 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#include "config.h" -#ifdef FEATURE_SOCKET - -#ifndef _IO_H -#define _IO_H +#ifndef _EXT_UTIL_H +#define _EXT_UTIL_H #include -gboolean io_init_socket(const char *name); -void io_cleanup(void); +gboolean ext_util_create_tmp_file(const char *content, char **file); -#endif /* end of include guard: _IO_H */ -#endif +#endif /* end of include guard: _EXT_UTIL_H */ diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index 766f66ed..00000000 --- a/tests/Makefile +++ /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 index 3accc379..00000000 --- a/tests/manual/112-editable-focus.html +++ /dev/null @@ -1,39 +0,0 @@ - - -Input mode Switching (#112 #237) - - - -

- Run with scripts=on and strict-focus=off -

    -
  1. If page is loade, vimb should be in input mode.
  2. -
  3. Set strict-focus=on and reload page. Vimb should keep - in normal mode
  4. -
  5. Independent from the strict-focus should vimb switch - to input mode if the button is clicked
  6. -
-

-
-
-
- -
-
-

- Also the following element using contenteditable="true" - should switch vimb into input mode on click. -

-
- Clicking this element using contenteditable="true" should - switch vimb into input mode too. -
- - - diff --git a/tests/manual/146-hsts-iframe.html b/tests/manual/146-hsts-iframe.html deleted file mode 100644 index 7775b753..00000000 --- a/tests/manual/146-hsts-iframe.html +++ /dev/null @@ -1,14 +0,0 @@ - - -iFrame URI change by HSTS (#146) - - -

- The hsts domain used in iFrame with http must not lead to load the - iframe content as page. -

- - - diff --git a/tests/manual/201-editable-focus-in-iframes.html b/tests/manual/201-editable-focus-in-iframes.html deleted file mode 100644 index d02c0fb5..00000000 --- a/tests/manual/201-editable-focus-in-iframes.html +++ /dev/null @@ -1,9 +0,0 @@ - - -Track Focu/Blur also within iFrames - - - - - - diff --git a/tests/test-handlers.c b/tests/test-handlers.c deleted file mode 100644 index 8cff0b1a..00000000 --- a/tests/test-handlers.c +++ /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 -#include - -#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 index 8fe5ace8..00000000 --- a/tests/test-map.c +++ /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 -#include -#include -#include -#include - -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]"); - ASSERT_MAPPING("", "[shift-tab]"); - ASSERT_MAPPING("", "[ctrl-f]"); - ASSERT_MAPPING("", "[ctrl-f]"); - ASSERT_MAPPING("", "[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 and CTRL-M like */ - ASSERT_MAPPING("", "[tab]"); - ASSERT_MAPPING("", "[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", "[d]", 't', false); - map_insert("[c]", "c", 't', false); /* inlen > mappedlen */ - map_insert("foobar", "[baz]", 't', false); - map_insert("", "[tab]", 't', false); - map_insert("", "[shift-tab]", 't', false); - map_insert("", "[ctrl-f]", 't', false); - map_insert("", "[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 index 697a04df..00000000 --- a/tests/test-shortcut.c +++ /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 -#include - -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 index 67b1954c..00000000 --- a/tests/test-util.c +++ /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 -#include - -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(); -} From 7edb3dfebc4e83fd1d01cb4b7284cf93f12cf163 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 15 Apr 2016 23:54:39 +0200 Subject: [PATCH 002/201] Fix none applied status style. --- src/config.def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index da33cc76..9aecde07 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -37,9 +37,9 @@ /* 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;} \ +#define GUI_STYLE "#statusbar{color:#fff;background-color:#000;font:monospace bold 10;} \ +#statusbar.secure{background-color:#95e454;color:#000;} \ +#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;} \ From 0938b3fd432c2320d143b14be1bf54457f94d5f5 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 1 Jun 2016 23:00:01 +0200 Subject: [PATCH 003/201] Update license year. --- src/ascii.h | 2 +- src/command.c | 2 +- src/command.h | 2 +- src/completion.c | 2 +- src/completion.h | 2 +- src/config.def.h | 2 +- src/ex.c | 2 +- src/ex.h | 2 +- src/ext-proxy.c | 2 +- src/ext-proxy.h | 2 +- src/history.c | 2 +- src/history.h | 2 +- src/input.c | 2 +- src/input.h | 2 +- src/js.c | 2 +- src/js.h | 2 +- src/main.c | 2 +- src/main.h | 2 +- src/map.c | 2 +- src/map.h | 2 +- src/normal.c | 2 +- src/normal.h | 2 +- src/setting.c | 2 +- src/setting.h | 2 +- src/shortcut.c | 2 +- src/shortcut.h | 2 +- src/util.c | 2 +- src/util.h | 2 +- src/webextension/ext-dom.c | 2 +- src/webextension/ext-dom.h | 2 +- src/webextension/ext-main.c | 2 +- src/webextension/ext-main.h | 2 +- src/webextension/ext-util.c | 2 +- src/webextension/ext-util.h | 2 +- 34 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/ascii.h b/src/ascii.h index f375a302..d85ef688 100644 --- a/src/ascii.h +++ b/src/ascii.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/command.c b/src/command.c index c957c463..09ab5ef9 100644 --- a/src/command.c +++ b/src/command.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/command.h b/src/command.h index 92cf4941..ba2ba46b 100644 --- a/src/command.h +++ b/src/command.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/completion.c b/src/completion.c index 8bfb9b03..714ddcf8 100644 --- a/src/completion.c +++ b/src/completion.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/completion.h b/src/completion.h index e353ecba..8c8d2286 100644 --- a/src/completion.h +++ b/src/completion.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/config.def.h b/src/config.def.h index 9aecde07..fdcb23a1 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/ex.c b/src/ex.c index 914a5549..2d0f2e46 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/ex.h b/src/ex.h index 1173d90a..ec0960d3 100644 --- a/src/ex.h +++ b/src/ex.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 75ccbd77..0fe07b8c 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/ext-proxy.h b/src/ext-proxy.h index b5b33a16..3d6ae141 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/history.c b/src/history.c index fcd89fce..b2bb1d47 100644 --- a/src/history.c +++ b/src/history.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/history.h b/src/history.h index 8709f5d6..c074d062 100644 --- a/src/history.h +++ b/src/history.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/input.c b/src/input.c index b5381ffd..afa93606 100644 --- a/src/input.c +++ b/src/input.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/input.h b/src/input.h index 64d1d5c1..29a07995 100644 --- a/src/input.h +++ b/src/input.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/js.c b/src/js.c index e6f467ef..a65c41c2 100644 --- a/src/js.c +++ b/src/js.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/js.h b/src/js.h index 1a63d0a3..fd6158b5 100644 --- a/src/js.h +++ b/src/js.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/main.c b/src/main.c index a8c9f74d..a91cde3f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/main.h b/src/main.h index f0816d04..2db4696e 100644 --- a/src/main.h +++ b/src/main.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/map.c b/src/map.c index 5b4017a9..d5b1768b 100644 --- a/src/map.c +++ b/src/map.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/map.h b/src/map.h index 4d5308ad..d97d1b42 100644 --- a/src/map.h +++ b/src/map.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/normal.c b/src/normal.c index 2980ecf5..3398488d 100644 --- a/src/normal.c +++ b/src/normal.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/normal.h b/src/normal.h index e4f626c8..912d2c64 100644 --- a/src/normal.h +++ b/src/normal.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/setting.c b/src/setting.c index 79d3ebb3..a3ad8a69 100644 --- a/src/setting.c +++ b/src/setting.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/setting.h b/src/setting.h index cd1d8d50..93f7182c 100644 --- a/src/setting.h +++ b/src/setting.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/shortcut.c b/src/shortcut.c index 0663b9f8..7545037e 100644 --- a/src/shortcut.c +++ b/src/shortcut.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/shortcut.h b/src/shortcut.h index f932abf1..9b44e29f 100644 --- a/src/shortcut.h +++ b/src/shortcut.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/util.c b/src/util.c index 9670b848..be48e711 100644 --- a/src/util.c +++ b/src/util.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/util.h b/src/util.h index c09b0987..94a60ca7 100644 --- a/src/util.h +++ b/src/util.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-dom.c b/src/webextension/ext-dom.c index 26aeaf0f..911e4e36 100644 --- a/src/webextension/ext-dom.c +++ b/src/webextension/ext-dom.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-dom.h b/src/webextension/ext-dom.h index 657821d0..795754c4 100644 --- a/src/webextension/ext-dom.h +++ b/src/webextension/ext-dom.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index 669fd7db..a3a8a838 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-main.h b/src/webextension/ext-main.h index 61788004..ff3f0a30 100644 --- a/src/webextension/ext-main.h +++ b/src/webextension/ext-main.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-util.c b/src/webextension/ext-util.c index 0bb40e41..f63cb11b 100644 --- a/src/webextension/ext-util.c +++ b/src/webextension/ext-util.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 diff --git a/src/webextension/ext-util.h b/src/webextension/ext-util.h index 452c4276..a4286a5a 100644 --- a/src/webextension/ext-util.h +++ b/src/webextension/ext-util.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2015 Daniel Carl + * Copyright (C) 2012-2016 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 From 2d5a6a36d81a25798f50f6e03395d90c94b4fac2 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 16 Jun 2016 22:46:51 +0200 Subject: [PATCH 004/201] Prepare the style provider only once. Added names for the main styled gui elements for easier styling via css. --- src/completion.c | 5 ++--- src/main.c | 11 +++++++---- src/main.h | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/completion.c b/src/completion.c index 714ddcf8..8bda61f0 100644 --- a/src/completion.c +++ b/src/completion.c @@ -98,11 +98,10 @@ gboolean completion_create(Client *c, GtkTreeModel *model, 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(vb.style_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_widget_set_name(GTK_WIDGET(comp->tree), "completion"); 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); diff --git a/src/main.c b/src/main.c index a91cde3f..982ec7cf 100644 --- a/src/main.c +++ b/src/main.c @@ -559,13 +559,11 @@ static Client *client_new(WebKitWebView *webview) 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(vb.style_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(vb.style_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); /* set the x window id to env */ @@ -1075,6 +1073,7 @@ static void vimb_setup(void) * 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_web_process_count_limit(ctx, 1); webkit_web_context_set_cache_model(ctx, WEBKIT_CACHE_MODEL_WEB_BROWSER); g_signal_connect(ctx, "initialize-web-extensions", G_CALLBACK(on_webctx_init_web_extension), NULL); @@ -1096,6 +1095,10 @@ static void vimb_setup(void) 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); + + /* Prepare the style provider to be used for the clients and completion. */ + vb.style_provider = gtk_css_provider_get_default(); + gtk_css_provider_load_from_data(vb.style_provider, GUI_STYLE, -1, NULL); } /** diff --git a/src/main.h b/src/main.h index 2db4696e..b4af03e8 100644 --- a/src/main.h +++ b/src/main.h @@ -255,6 +255,7 @@ struct Vimb { struct { guint history_max; } config; + GtkCssProvider *style_provider; }; From a9b302273d0d519c1b5a1dd6d1ff732ef08e7d83 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 16 Jun 2016 23:19:41 +0200 Subject: [PATCH 005/201] Adapted default gui style to new naming of widgets. --- src/config.def.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index fdcb23a1..3f85383b 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -37,14 +37,15 @@ /* 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 "#statusbar{color:#fff;background-color:#000;font:monospace bold 10;} \ +#define GUI_STYLE "\ +#statusbar{color:#fff;background-color:#000;font:monospace bold 10;} \ #statusbar.secure{background-color:#95e454;color:#000;} \ #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;}" +#input{background-color:#fff;color:#000;font:monospace 10;} \ +#input.error{background-color:#f77;font-weight:bold;} \ +#completion{color:#fff;background-color:#656565;font:monospace 10;} \ +#completion:hover{background-color:#777;} \ +#completion:selected{color:#f6f3e8;background-color:#888;}" /* default font size for fonts in webview */ #define SETTING_DEFAULT_FONT_SIZE 10 From 6ac60d8e8eae8a5e1b7b2d64b01732a0b63832b2 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 16 Jun 2016 23:48:55 +0200 Subject: [PATCH 006/201] Show progress in window title. --- src/config.def.h | 9 ++++++--- src/main.c | 26 +++++++++++++++++++++++++- src/main.h | 1 + 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index 3f85383b..cc3b915c 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -22,13 +22,16 @@ #define FEATURE_HISTORY_INDICATOR /* show wget style progressbar in status bar */ #define FEATURE_WGET_PROGRESS_BAR +/* show load progress in window title */ +#define FEATURE_TITLE_PROGRESS +/* show page title in url completions */ +#define FEATURE_TITLE_IN_COMPLETION + #ifdef FEATURE_WGET_PROGRESS_BAR /* chars to use for the progressbar */ -#define PROGRESS_BAR "=> " +#define PROGRESS_BAR "=> " #define PROGRESS_BAR_LEN 20 #endif -/* 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 */ diff --git a/src/main.c b/src/main.c index 982ec7cf..39d87700 100644 --- a/src/main.c +++ b/src/main.c @@ -70,6 +70,7 @@ 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_title(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); @@ -683,7 +684,9 @@ static void set_statusbar_style(Client *c, StatusType type) */ static void set_title(Client *c, const char *title) { - gtk_window_set_title(GTK_WINDOW(c->window), title); + OVERWRITE_STRING(c->state.title, title); + update_title(c); + g_setenv("VIMB_TITLE", title ? title : "", true); } /** @@ -909,6 +912,7 @@ static void on_webview_notify_estimated_load_progress(WebKitWebView *webview, { c->state.progress = webkit_web_view_get_estimated_load_progress(webview) * 100; vb_statusbar_update(c); + update_title(c); } /** @@ -983,6 +987,26 @@ static void register_cleanup(Client *c) } } +static void update_title(Client *c) +{ +#ifdef FEATURE_TITLE_PROGRESS + /* Show load status of page or the downloads. */ + if (c->state.progress != 100) { + char *title = g_strdup_printf( + "[%i%%] %s", + c->state.progress, + c->state.title ? c->state.title : ""); + gtk_window_set_title(GTK_WINDOW(c->window), title); + g_free(title); + + return; + } +#endif + if (c->state.title) { + gtk_window_set_title(GTK_WINDOW(c->window), c->state.title); + } +} + /** * 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. diff --git a/src/main.h b/src/main.h index b4af03e8..6e91291e 100644 --- a/src/main.h +++ b/src/main.h @@ -162,6 +162,7 @@ struct State { StatusType status_type; glong scroll_max; /* Maxmimum scrollable height of the document. */ guint scroll_percent; /* Current position of the viewport in document. */ + char *title; /* Window title of the client. */ char *reg[REG_SIZE]; /* holds the yank buffers */ /* TODO rename to reg_{enabled,current} */ From f85e43d06957e517bbae0e62fd0e9486d6246b66 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 17 Jun 2016 00:00:46 +0200 Subject: [PATCH 007/201] Don't use deprecated node list api. --- src/webextension/ext-dom.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/webextension/ext-dom.c b/src/webextension/ext-dom.c index 911e4e36..7c0b7cd6 100644 --- a/src/webextension/ext-dom.c +++ b/src/webextension/ext-dom.c @@ -83,19 +83,19 @@ gboolean ext_dom_is_editable(WebKitDOMElement *element) gboolean ext_dom_focus_input(WebKitDOMDocument *doc) { WebKitDOMNode *html, *node; - WebKitDOMNodeList *list; + WebKitDOMHTMLCollection *collection; WebKitDOMXPathNSResolver *resolver; WebKitDOMXPathResult* result; WebKitDOMDocument *frame_doc; guint i, len; - list = webkit_dom_document_get_elements_by_tag_name(doc, "html"); - if (!list) { + collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection(doc, "html"); + if (!collection) { return FALSE; } - html = webkit_dom_node_list_item(list, 0); - g_object_unref(list); + html = webkit_dom_html_collection_item(collection, 0); + g_object_unref(collection); resolver = webkit_dom_document_create_ns_resolver(doc, html); if (!resolver) { @@ -134,19 +134,19 @@ gboolean ext_dom_focus_input(WebKitDOMDocument *doc) } /* 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); + collection = webkit_dom_document_get_elements_by_tag_name_as_html_collection(doc, "iframe"); + len = webkit_dom_html_collection_get_length(collection); for (i = 0; i < len; i++) { - node = webkit_dom_node_list_item(list, i); + node = webkit_dom_html_collection_item(collection, 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); + g_object_unref(collection); return TRUE; } } - g_object_unref(list); + g_object_unref(collection); return FALSE; } From 90afd399fd58f2084fd8c74dcc7de7511f46b02f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 20 Jun 2016 22:11:17 +0200 Subject: [PATCH 008/201] Enable registers to continue a search. --- src/main.c | 2 ++ src/main.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 39d87700..bc1496ed 100644 --- a/src/main.c +++ b/src/main.c @@ -577,6 +577,8 @@ static Client *client_new(WebKitWebView *webview) /* start client in normal mode */ vb_enter(c, 'n'); + c->state.enable_register = TRUE; + gtk_widget_show_all(c->window); /* Prepend the new client to the queue of clients. */ diff --git a/src/main.h b/src/main.h index 6e91291e..c1e925eb 100644 --- a/src/main.h +++ b/src/main.h @@ -187,7 +187,6 @@ struct Map { 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 { From 2dc1b9df59d9d9cb78869637ec4ba017fcae6d35 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 20 Jun 2016 22:15:22 +0200 Subject: [PATCH 009/201] Mark search as done. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2e03318..126946fd 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ project directory. - [ ] editor command - [ ] external downloader - [ ] hinting - - [ ] searching and matching of search results + - [x] searching and matching of search results - [ ] navigation j, k, h, l, ... - [ ] history and history lookup - [ ] completion From fcedbe1fea08239650db9c151cac5022bbaa3782 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 29 Jun 2016 23:26:26 +0200 Subject: [PATCH 010/201] Found a nice way to disable scrollbars. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 126946fd..42beb577 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,9 @@ project directory. - [ ] 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 + - [x] find a way to disable the scrollbars on the main frame + Can be achieved by `document.documentElement.style.overflow = + 'hidden';` in _scripts.js_ - [ ] 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) From a2642b38586c7e530b8beaf9aac0594c1c8751c8 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 29 Jun 2016 23:49:12 +0200 Subject: [PATCH 011/201] Allow to set config file per option --config, -c. --- README.md | 2 +- src/ex.c | 83 +++++++++++++++++++++++++++++++++++++----------------- src/ex.h | 1 + src/main.c | 3 ++ 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 42beb577..c80bd9a2 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ project directory. - [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 + - [x] 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 diff --git a/src/ex.c b/src/ex.c index 2d0f2e46..e3a5a712 100644 --- a/src/ex.c +++ b/src/ex.c @@ -408,6 +408,63 @@ gboolean ex_fill_completion(GtkListStore *store, const char *input) return found; } +/** + * Run all ex commands from a file. + */ +VbCmdResult ex_run_file(Client *c, const char *filename) +{ + int length, i; + char *line, **lines; + VbCmdResult res = CMD_SUCCESS; + + lines = util_get_lines(filename); + if (!lines) { + return res; + } + + length = g_strv_length(lines) - 1; + for (i = 0; i < length; i++) { + line = lines[i]; + /* skip commented or empty lines */ + if (*line == '#' || !*line) { + continue; + } + if ((ex_run_string(c, line, false) & ~CMD_KEEPINPUT) == CMD_ERROR) { + res = CMD_ERROR | CMD_KEEPINPUT; + g_warning("Invalid command in %s: '%s'", filename, line); + } + } + g_strfreev(lines); + + return res; +} + +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 = 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(c, &in, arg, &nohist) || !(res = execute(c, arg))) { + break; + } + } + + if (enable_history && !nohist) { + history_add(c, HISTORY_COMMAND, input, NULL); + vb_register_add(c, ':', input); + } + + free_cmdarg(arg); + + return res; +} + /** * This is called if the user typed or into the inputbox. */ @@ -446,32 +503,6 @@ static void input_activate(Client *c) g_free(text); } -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 = 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(c, &in, arg, &nohist) || !(res = execute(c, arg))) { - break; - } - } - - if (enable_history && !nohist) { - history_add(c, HISTORY_COMMAND, input, NULL); - vb_register_add(c, ':', input); - } - - free_cmdarg(arg); - - return res; -} - /** * Parses given input string into given ExArg pointer. */ diff --git a/src/ex.h b/src/ex.h index ec0960d3..1fd057f2 100644 --- a/src/ex.h +++ b/src/ex.h @@ -28,6 +28,7 @@ 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_file(Client *c, 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/main.c b/src/main.c index bc1496ed..8644123a 100644 --- a/src/main.c +++ b/src/main.c @@ -581,6 +581,9 @@ static Client *client_new(WebKitWebView *webview) gtk_widget_show_all(c->window); + /* read the config file */ + ex_run_file(c, vb.files[FILES_CONFIG]); + /* Prepend the new client to the queue of clients. */ c->next = vb.clients; vb.clients = c; From 92bbe9c75c0c55d277fa69590d892b278bf2e05e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 30 Jun 2016 00:07:50 +0200 Subject: [PATCH 012/201] Reorder Makefile a little. --- Makefile | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index b7abcfd9..eb5b82b2 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,14 @@ options: @echo "LDFLAGS = $(LDFLAGS)" @echo "CC = $(CC)" +vimb: $(SUBDIRS:%=%.subdir-all) + +%.subdir-all: + $(MAKE) $(MFLAGS) -C $* + +%.subdir-clean: + $(MAKE) $(MFLAGS) -C $* clean + install: vimb @# binary install -d $(BINPREFIX) @@ -16,24 +24,20 @@ install: vimb @# extension install -d $(EXTPREFIX) install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET) + install -d $(DESTDIR)$(MANDIR)/man1 + @sed -e "s!VERSION!$(VERSION)!g" \ + -e "s!PREFIX!$(PREFIX)!g" \ + -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(DESTDIR)$(MANDIR)/man1/vimb.1 uninstall: - $(RM) $(BINPREFIX)/vimb $(EXTPREFIX)/$(EXTTARGET) - -vimb: $(SUBDIRS:%=%.subdir-all) - -%.subdir-all: - $(MAKE) $(MFLAGS) -C $* - -%.subdir-clean: - $(MAKE) $(MFLAGS) -C $* clean + $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) clean: $(SUBDIRS:%=%.subdir-clean) -runsandbox: sandbox - sandbox/usr/bin/vimb - sandbox: make RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install -.PHONY: all options clean install uninstall sandbox +runsandbox: sandbox + sandbox/usr/bin/vimb + +.PHONY: all options clean install uninstall sandbox sandbox-clean From 79366c7c4710bfdb4697eb5b0a82a7472bc76482 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 30 Jun 2016 00:53:20 +0200 Subject: [PATCH 013/201] Made make a bit more silent. --- Makefile | 6 +++--- config.mk | 1 + src/webextension/Makefile | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index eb5b82b2..57731e0e 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ options: vimb: $(SUBDIRS:%=%.subdir-all) %.subdir-all: - $(MAKE) $(MFLAGS) -C $* + @$(MAKE) $(MFLAGS) -C $* %.subdir-clean: - $(MAKE) $(MFLAGS) -C $* clean + @$(MAKE) $(MFLAGS) -C $* clean install: vimb @# binary @@ -35,7 +35,7 @@ uninstall: clean: $(SUBDIRS:%=%.subdir-clean) sandbox: - make RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install + @make $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install runsandbox: sandbox sandbox/usr/bin/vimb diff --git a/config.mk b/config.mk index e3f18005..4a7dae54 100644 --- a/config.mk +++ b/config.mk @@ -35,3 +35,4 @@ EXTLDFLAGS = $(shell pkg-config --libs webkit2gtk-4.0) -shared CFLAGS += $(shell pkg-config --cflags $(LIBS)) CFLAGS += ${CPPFLAGS} LDFLAGS += $(shell pkg-config --libs $(LIBS)) +MFLAGS = --no-print-directory diff --git a/src/webextension/Makefile b/src/webextension/Makefile index 671c23f0..c15c0885 100644 --- a/src/webextension/Makefile +++ b/src/webextension/Makefile @@ -6,7 +6,7 @@ OBJ = $(patsubst %.c, %.lo, $(wildcard *.c)) all: $(EXTTARGET) clean: - $(RM) -f $(EXTTARGET) *.lo + $(RM) $(EXTTARGET) *.lo $(EXTTARGET): $(OBJ) @echo "$(CC) $@" From 005a5875c3189ab6e82eabda7bce011251412373 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 18 Sep 2016 23:48:16 +0200 Subject: [PATCH 014/201] Disables Makefile man page processing, man page missing for now --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 57731e0e..3ca776fd 100644 --- a/Makefile +++ b/Makefile @@ -25,9 +25,9 @@ install: vimb install -d $(EXTPREFIX) install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET) install -d $(DESTDIR)$(MANDIR)/man1 - @sed -e "s!VERSION!$(VERSION)!g" \ - -e "s!PREFIX!$(PREFIX)!g" \ - -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(DESTDIR)$(MANDIR)/man1/vimb.1 + # @sed -e "s!VERSION!$(VERSION)!g" \ + # -e "s!PREFIX!$(PREFIX)!g" \ + # -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(DESTDIR)$(MANDIR)/man1/vimb.1 uninstall: $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) From 67407191572c44edec10f1ec045c5f2a337cb2c2 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 19 Sep 2016 00:03:16 +0200 Subject: [PATCH 015/201] Fixes man page install rule --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3ca776fd..0201ccb6 100644 --- a/Makefile +++ b/Makefile @@ -24,10 +24,11 @@ install: vimb @# extension install -d $(EXTPREFIX) install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET) - install -d $(DESTDIR)$(MANDIR)/man1 + @# man page + install -d $(MANPREFIX)/man1 # @sed -e "s!VERSION!$(VERSION)!g" \ # -e "s!PREFIX!$(PREFIX)!g" \ - # -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(DESTDIR)$(MANDIR)/man1/vimb.1 + # -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(MANPREFIX)/man1/vimb.1 uninstall: $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) From 70a3fd8ca932aba8f3046f83a1d63c76cda94714 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 19 Sep 2016 00:31:51 +0200 Subject: [PATCH 016/201] Adds default-zoom setting --- README.md | 1 + src/setting.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/README.md b/README.md index c80bd9a2..c79c9a61 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ project directory. instances and to work also if the page was reloaded or changed like the marks in vim) - [x] zooming + - [x] default zoom - [ ] yanking - [x] keymapping - [ ] URL handler diff --git a/src/setting.c b/src/setting.c index a3ad8a69..373a9db3 100644 --- a/src/setting.c +++ b/src/setting.c @@ -49,6 +49,7 @@ static void setting_print(Client *c, Setting *s); static void setting_free(Setting *s); static int cookie_accept(Client *c, const char *name, DataType type, void *value, void *data); +static int default_zoom(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); @@ -137,6 +138,8 @@ void setting_init(Client *c) 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); + i = 100; + setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); /* initialize the shortcuts and set the default shortcuts */ shortcut_init(c); @@ -464,6 +467,16 @@ static int cookie_accept(Client *c, const char *name, DataType type, void *value return CMD_SUCCESS; } +static int default_zoom(Client *c, const char *name, DataType type, void *value, void *data) +{ + float zoom = (float)*(int*)value / 100.0; + + webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(c->webview), FALSE); + webkit_web_view_set_zoom_level(c->webview, zoom); + + return CMD_SUCCESS; +} + static int fullscreen(Client *c, const char *name, DataType type, void *value, void *data) { if (*(gboolean*)value) { From c4b159a1ba42d4456ba918a5925c71917f6ce44a Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 2 Aug 2016 22:54:26 +0200 Subject: [PATCH 017/201] Mark navigation as done. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c79c9a61..b22b6934 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ project directory. - [ ] external downloader - [ ] hinting - [x] searching and matching of search results - - [ ] navigation j, k, h, l, ... + - [x] navigation j, k, h, l, ... - [ ] history and history lookup - [ ] completion - [ ] HSTS From 12d53b57e29807d619c114cdd0bef76bc96772a7 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 28 Sep 2016 00:03:32 +0200 Subject: [PATCH 018/201] Show output of :eval in inputbox. --- src/ex.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- src/main.c | 1 + 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/ex.c b/src/ex.c index e3a5a712..98570d1a 100644 --- a/src/ex.c +++ b/src/ex.c @@ -22,6 +22,7 @@ * commands from inputbox and the ex commands. */ +#include #include #include @@ -121,6 +122,8 @@ 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 void ex_eval_javascript_finished(GObject *object, + GAsyncResult *result, Client *c); 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); @@ -747,11 +750,51 @@ static VbCmdResult ex_bookmark(Client *c, const ExArg *arg) static VbCmdResult ex_eval(Client *c, const ExArg *arg) { - /* TODO allow to get the return value and possible errors. */ - webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, NULL, NULL); + /* Called as :eval! - don't print to inputbox. */ + if (arg->bang) { + webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, NULL, NULL); + } else { + webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, + (GAsyncReadyCallback)ex_eval_javascript_finished, c); + } + return CMD_SUCCESS; } +static void ex_eval_javascript_finished(GObject *object, + GAsyncResult *result, Client *c) +{ + WebKitJavascriptResult *js_result; + JSValueRef value; + JSGlobalContextRef context; + GError *error = NULL; + + js_result = webkit_web_view_run_javascript_finish(WEBKIT_WEB_VIEW(object), result, &error); + if (!js_result) { + vb_echo(c, MSG_ERROR, TRUE, "%s", error->message); + g_error_free(error); + + return; + } + + context = webkit_javascript_result_get_global_context(js_result); + value = webkit_javascript_result_get_value(js_result); + if (JSValueIsString(context, value)) { + JSStringRef str_ref; + char *string; + size_t len; + + str_ref = JSValueToStringCopy(context, value, NULL); + len = JSStringGetMaximumUTF8CStringSize(str_ref); + string = g_new(char, len); + JSStringGetUTF8CString(str_ref, string, len); + JSStringRelease(str_ref); + vb_echo(c, MSG_NORMAL, FALSE, "%s", string); + g_free(string); + } + webkit_javascript_result_unref(js_result); +} + static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg) { /* TODO no implemented yet */ diff --git a/src/main.c b/src/main.c index 8644123a..93141ad6 100644 --- a/src/main.c +++ b/src/main.c @@ -622,6 +622,7 @@ static void input_print(Client *c, gboolean force, MessageType type, /* apply input style only if the message type was changed */ if (type != c->state.input_type) { c->state.input_type = type; + vb_input_update_style(c); } vb_input_set_text(c, message); if (hide) { From ff51de70107faf42b0ef0e3772acd0b7b01ae50b Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 28 Sep 2016 00:29:27 +0200 Subject: [PATCH 019/201] Allow link activation from search result. When a search is performed and the current highlighted result is part of a link, a click event is triggered on the link to open it. Currently the click() is done by JavaScript on the element so that we can't control if the target open in current window or in a new one. --- src/command.c | 1 + src/normal.c | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 09ab5ef9..9d97f426 100644 --- a/src/command.c +++ b/src/command.c @@ -68,6 +68,7 @@ gboolean command_search(Client *c, const Arg *arg) WEBKIT_FIND_OPTIONS_WRAP_AROUND | (forward ? WEBKIT_FIND_OPTIONS_NONE : WEBKIT_FIND_OPTIONS_BACKWARDS), G_MAXUINT); + /* TODO get the number of matches */ c->state.search.active = TRUE; /* Skip first search because the first match is already * highlighted on search start. */ diff --git a/src/normal.c b/src/normal.c index 3398488d..7410fb9e 100644 --- a/src/normal.c +++ b/src/normal.c @@ -53,6 +53,7 @@ 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_fire(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); @@ -90,7 +91,7 @@ static struct { /* ^J 0x0a */ {NULL}, /* ^K 0x0b */ {NULL}, /* ^L 0x0c */ {NULL}, -/* ^M 0x0d */ {NULL}, +/* ^M 0x0d */ {normal_fire}, /* ^N 0x0e */ {NULL}, /* ^O 0x0f */ {normal_navigate}, /* ^P 0x10 */ {normal_queue}, @@ -415,6 +416,22 @@ static VbResult normal_ex(Client *c, const NormalCmdInfo *info) return RESULT_COMPLETE; } +static VbResult normal_fire(Client *c, const NormalCmdInfo *info) +{ + /* If searching is currently active - click link containing current search + * highlight. We use the search_matches as indicator that the searching is + * active. */ + if (c->state.search.active) { + webkit_web_view_run_javascript(c->webview, + "getSelection().anchorNode.parentNode.click();", NULL, NULL, + NULL); + + return RESULT_COMPLETE; + } + + return RESULT_ERROR; +} + static VbResult normal_g_cmd(Client *c, const NormalCmdInfo *info) { Arg a; From 1a77685e0b3be694b65a930734dd06986e811434 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 25 Sep 2016 22:35:22 +0200 Subject: [PATCH 020/201] Adds yank command --- README.md | 2 +- src/command.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b22b6934..ad60253e 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ project directory. the marks in vim) - [x] zooming - [x] default zoom - - [ ] yanking + - [x] yanking - [x] keymapping - [ ] URL handler - [x] shortcuts diff --git a/src/command.c b/src/command.c index 9d97f426..a8495e0b 100644 --- a/src/command.c +++ b/src/command.c @@ -91,7 +91,49 @@ gboolean command_search(Client *c, const Arg *arg) gboolean command_yank(Client *c, const Arg *arg, char buf) { - /* TODO no implemented yet */ + /** + * This implementation is quite 'brute force', same as in vimb2 + * - both X clipboards are always set, PRIMARY and CLIPBOARD + * - the X clipboards are always set, even though a vimb register was given + */ + + const char *uri = NULL; + char *yanked = NULL; + + g_assert(c); + g_assert(arg); + g_assert(c->webview); + g_assert(arg->i == COMMAND_YANK_URI || arg->i == COMMAND_YANK_SELECTION); + + if (arg->i == COMMAND_YANK_URI) { + if ((uri = webkit_web_view_get_uri(c->webview))) { + yanked = g_strdup(uri); + } + } else if (arg->i == COMMAND_YANK_SELECTION) { + // copy web view selection to clipboard + webkit_web_view_execute_editing_command(c->webview, WEBKIT_EDITING_COMMAND_COPY); + // read back copy from clipboard + yanked = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); + } + + if(!yanked) { + return FALSE; + } + + // store in vimb default register + vb_register_add(c, '"', yanked); + // store in vimb register buf if buf != 0 + vb_register_add(c, buf, yanked); + + // store in X clipboard primary (selected text copy, middle mouse paste) + gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), yanked, -1); + // store in X "windows style" clipboard + gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), yanked, -1); + + vb_echo(c, MSG_NORMAL, false, "Yanked: %s", yanked); + + g_free(yanked); + return TRUE; } From 596d0e05b9aa4007a02ecf40936d3a9340fec143 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Tue, 27 Sep 2016 21:27:36 +0200 Subject: [PATCH 021/201] Fixes comment style --- src/command.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/command.c b/src/command.c index a8495e0b..700bd2ce 100644 --- a/src/command.c +++ b/src/command.c @@ -110,9 +110,9 @@ gboolean command_yank(Client *c, const Arg *arg, char buf) yanked = g_strdup(uri); } } else if (arg->i == COMMAND_YANK_SELECTION) { - // copy web view selection to clipboard + /* copy web view selection to clipboard */ webkit_web_view_execute_editing_command(c->webview, WEBKIT_EDITING_COMMAND_COPY); - // read back copy from clipboard + /* read back copy from clipboard */ yanked = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); } @@ -120,14 +120,14 @@ gboolean command_yank(Client *c, const Arg *arg, char buf) return FALSE; } - // store in vimb default register + /* store in vimb default register */ vb_register_add(c, '"', yanked); - // store in vimb register buf if buf != 0 + /* store in vimb register buf if buf != 0 */ vb_register_add(c, buf, yanked); - // store in X clipboard primary (selected text copy, middle mouse paste) + /* store in X clipboard primary (selected text copy, middle mouse paste) */ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), yanked, -1); - // store in X "windows style" clipboard + /* store in X "windows style" clipboard */ gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), yanked, -1); vb_echo(c, MSG_NORMAL, false, "Yanked: %s", yanked); From d98b47e362227a1fc0a1be78f57b02d7f6740a6f Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 18 Sep 2016 23:50:34 +0200 Subject: [PATCH 022/201] Adds vimb.desktop file and install rule --- Makefile | 3 +++ config.mk | 13 +++++++------ vimb.desktop | 11 +++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 vimb.desktop diff --git a/Makefile b/Makefile index 0201ccb6..912bf669 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,9 @@ install: vimb # @sed -e "s!VERSION!$(VERSION)!g" \ # -e "s!PREFIX!$(PREFIX)!g" \ # -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(MANPREFIX)/man1/vimb.1 + @# .desktop file + install -d $(DOTDESKTOPPREFIX) + install -m 644 vimb.desktop $(DOTDESKTOPPREFIX)/vimb.desktop uninstall: $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) diff --git a/config.mk b/config.mk index 4a7dae54..2bf9eed8 100644 --- a/config.mk +++ b/config.mk @@ -1,11 +1,12 @@ 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 +PREFIX ?= /usr/local +BINPREFIX := $(DESTDIR)$(PREFIX)/bin +MANPREFIX := $(DESTDIR)$(PREFIX)/share/man +EXAMPLEPREFIX := $(DESTDIR)$(PREFIX)/share/vimb/example +DOTDESKTOPPREFIX := $(DESTDIR)$(PREFIX)/share/applications +RUNPREFIX := $(PREFIX) +EXTPREFIX := $(RUNPREFIX)/lib/vimb # define some directories SRCDIR = src diff --git a/vimb.desktop b/vimb.desktop new file mode 100644 index 00000000..37fa6e90 --- /dev/null +++ b/vimb.desktop @@ -0,0 +1,11 @@ +# Based on Arch Linux' chromium.desktop +[Desktop Entry] +Name=vimb +GenericName=Web Browser +Comment=Access the Internet +Exec=vimb %U +Terminal=false +Icon= +Type=Application +Categories=GTK;Network;WebBrowser; +MimeType=text/html;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; From 9834c6c658f5b2e39b33529da9335fd9fb099806 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 19 Sep 2016 00:15:28 +0200 Subject: [PATCH 023/201] Mark cookie support as done. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad60253e..ef58d27b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ project directory. - [ ] completion - [ ] HSTS - [ ] auto-response-header - - [ ] cookies support + - [x] cookies support - [ ] register support and `:register` command - [ ] read it later queue - [ ] show scroll indicator in statusbar as top, x%, bttom or all From ef2780d8e22b0ef2ef1d078bcb7cdc15b28fe89a Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Wed, 28 Sep 2016 22:00:36 +0200 Subject: [PATCH 024/201] Mark register support as done --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef58d27b..f10dfcb8 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ project directory. - [ ] HSTS - [ ] auto-response-header - [x] cookies support - - [ ] register support and `:register` command + - [x] 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? From dd69464ca6e832a1d1967e52bb7acfee770eb299 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 30 Sep 2016 09:59:03 +0200 Subject: [PATCH 025/201] Fix some typos. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f10dfcb8..0b23412f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you like to have a working vimb, please use the master branch. ├── doc documentation like manual page └── src all sources to build vimb - ├── scripts JavaScripts that are compiled in to various purposes + ├── scripts JavaScripts that are compiled in for various purposes └── webextension Source files for the webextension ## dependencies @@ -35,9 +35,9 @@ If you like to have a working vimb, please use the master branch. 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. +directory where the extension should be stored in. -To run vimb wihtout installation you could run as a sandbox like this +To run vimb without installation you could run as a sandbox like this make runsandbox @@ -50,7 +50,7 @@ project directory. - [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 + this is now available with the `RUNPREFIX` option 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 @@ -76,7 +76,7 @@ project directory. - [x] cookies support - [x] register support and `:register` command - [ ] read it later queue - - [ ] show scroll indicator in statusbar as top, x%, bttom or all + - [ ] show scroll indicator in statusbar as top, x%, bottom or all how can we get this information from webview easily? - [x] find a way to disable the scrollbars on the main frame Can be achieved by `document.documentElement.style.overflow = From a1f982e56162dc04facd6993116a5165b1f0d031 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Oct 2016 11:15:34 +0200 Subject: [PATCH 026/201] Allow to print page via :hardcopy command. --- src/ex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ex.c b/src/ex.c index 98570d1a..d1d38c5a 100644 --- a/src/ex.c +++ b/src/ex.c @@ -797,7 +797,7 @@ static void ex_eval_javascript_finished(GObject *object, static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ + webkit_web_view_run_javascript(c->webview, "window.print();", NULL, NULL, NULL); return CMD_SUCCESS; } From a4a6b0ff689c13e954552bbe9ba8fd7c822d449f Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 29 Sep 2016 17:35:29 +0200 Subject: [PATCH 027/201] Implements download path and tracking --- README.md | 2 +- src/main.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++++- src/main.h | 2 +- src/setting.c | 1 + 4 files changed, 192 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0b23412f..b46bc2f5 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ project directory. - [ ] 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 + - [x] download support - [ ] editor command - [ ] external downloader - [ ] hinting diff --git a/src/main.c b/src/main.c index 93141ad6..77d7af75 100644 --- a/src/main.c +++ b/src/main.c @@ -49,7 +49,14 @@ 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_download_started(WebKitWebContext *webctx, + WebKitDownload *download, Client *c); static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data); +static gboolean on_webdownload_decide_destination(WebKitDownload *download, + gchar *suggested_filename, Client *c); +static void on_webdownload_failed(WebKitDownload *download, + GError *error, Client *c); +static void on_webdownload_finished(WebKitDownload *download, Client *c); static void on_webview_close(WebKitWebView *webview, Client *c); static WebKitWebView *on_webview_create(WebKitWebView *webview, WebKitNavigationAction *navact, Client *c); @@ -348,13 +355,12 @@ void vb_modelabel_update(Client *c, const char *label) */ void vb_quit(Client *c, gboolean force) { -#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"); + vb_echo_force(c, 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); @@ -722,6 +728,22 @@ static void spawn_new_instance(const char *uri, gboolean embed) g_spawn_async(NULL, cmd, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); } +/** + * Callback for the web contexts download-started signal. + */ +static void on_webctx_download_started(WebKitWebContext *webctx, + WebKitDownload *download, Client *c) +{ + g_signal_connect(download, "decide-destination", G_CALLBACK(on_webdownload_decide_destination), c); + g_signal_connect(download, "failed", G_CALLBACK(on_webdownload_failed), c); + g_signal_connect(download, "finished", G_CALLBACK(on_webdownload_finished), c); + + c->state.downloads = g_list_append(c->state.downloads, download); + + /* to reflect the correct download count */ + vb_statusbar_update(c); +} + /** * Callback for the web contexts initialize-web-extensions signal. */ @@ -743,6 +765,168 @@ static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data g_free(name); } +/** + * Callback for the webkit download decide destination signal. + * This signal is emitted after response is received to decide a destination + * URI for the download. + */ +static gboolean on_webdownload_decide_destination(WebKitDownload *download, + gchar *suggested_filename, Client *c) +{ + g_assert(download); + g_assert(suggested_filename); + g_assert(c); + + char *path, *filename, *uri; + GString *expanded, *filepath; + const char *extension_dot; + int suffix; + gssize suffix_pos; + + /* get the download path from settings */ + path = GET_CHAR(c, "download-path"); + g_assert(path); + + /* expand any ~ or $VAR patterns in download path */ + expanded = g_string_new(NULL); + util_parse_expansion(c, (const char**)&path, expanded, UTIL_EXP_TILDE|UTIL_EXP_DOLLAR, ""); + g_string_append(expanded, path + 1); + + /* for unnamed downloads set default filename */ + filename = strlen(suggested_filename) ? suggested_filename : "vimb-download"; + + /* construct complete download filepath */ + filepath = g_string_new(NULL); + g_string_printf(filepath, "%s%c%s", expanded->str, G_DIR_SEPARATOR, filename); + + /* if the filepath exists already + * insert numerical suffix before file extension */ + if (g_file_test(filepath->str, G_FILE_TEST_EXISTS)) { + suffix = 2; + + /* position on .tar. (special case, extension with two dots), + * position on last dot (if any) otherwise */ + if (!(extension_dot = strstr(filename, ".tar."))) { + extension_dot = strrchr(filename, '.'); + } + + /* the position to insert the suffix at */ + if (extension_dot) { + suffix_pos = extension_dot - filename; + } else { + suffix_pos = strlen(filename); + } + + /* construct a new complete download filepath and add the suffix before + * the filename extension, keep incrementing the suffix value as long + * as the filepath exists, stop on first unused filename. */ + do { + g_string_printf(filepath, "%s%c%.*s_%i%s", + expanded->str, G_DIR_SEPARATOR, + (int)suffix_pos, filename, + suffix++, filename + suffix_pos); + } while (g_file_test(filepath->str, G_FILE_TEST_EXISTS)); + } + + /* build URI from filepath */ + uri = g_filename_to_uri(filepath->str, NULL, NULL); + g_assert(uri); + + /* configure download */ + webkit_download_set_allow_overwrite(download, FALSE); + webkit_download_set_destination(download, uri); + + /* cleanup */ + g_string_free(expanded, TRUE); + g_string_free(filepath, TRUE); + g_free(uri); + + return TRUE; +} + +/** + * Callback for the webkit download failed signal. + * This signal is emitted when an error occurs during the download operation. + */ +static void on_webdownload_failed(WebKitDownload *download, + GError *error, Client *c) +{ + gchar *destination = NULL, *filename = NULL, *basename = NULL; + + g_assert(download); + g_assert(error); + g_assert(c); + + /* get the failed download's destination uri */ + g_object_get(download, "destination", &destination, NULL); + g_assert(destination); + + /* filename from uri */ + if (destination) { + filename = g_filename_from_uri(destination, NULL, NULL); + g_free(destination); + } + + /* basename from filename */ + if (filename) { + basename = g_path_get_basename(filename); + g_free(filename); + } + + /* report the error to the user */ + if (basename) { + vb_echo(c, MSG_ERROR, FALSE, "Download of %s failed (%s)", basename, error->message); + g_free(basename); + } +} + +/** + * Callback for the webkit download finished signal. + * This signal is emitted when download finishes successfully or due to an + * error. In case of errors “failed” signal is emitted before this one. + */ +static void on_webdownload_finished(WebKitDownload *download, Client *c) +{ + gchar *destination = NULL, *filename = NULL, *basename = NULL; + + g_assert(download); + g_assert(c); + + c->state.downloads = g_list_remove(c->state.downloads, download); + + /* to reflect the correct download count */ + vb_statusbar_update(c); + + /* get the finished downloads destination uri */ + g_object_get(download, "destination", &destination, NULL); + g_assert(destination); + + /* filename from uri */ + if (destination) { + filename = g_filename_from_uri(destination, NULL, NULL); + g_free(destination); + } + + if (filename) { + /* basename from filename */ + basename = g_path_get_basename(filename); + + if (basename) { + /* Only report to the user if the downloaded file exists, so the + * download was successful. Otherwise, this is a failed download + * finished signal and it was reported to the user in + * on_webdownload_failed() already. */ + if (g_file_test(filename, G_FILE_TEST_EXISTS)) { + vb_echo(c, MSG_NORMAL, FALSE, "Download of %s finished", basename); + } + + g_free(basename); + } + + g_free(filename); + } +} + /** * Callback for the webview close signal. */ @@ -1168,6 +1352,8 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) NULL ); + g_signal_connect(webkit_web_context_get_default(), "download-started", G_CALLBACK(on_webctx_download_started), c); + /* Inject the user script file. */ if (g_file_get_contents(vb.files[FILES_SCRIPT], &js, NULL, NULL)) { script = webkit_user_script_new(js, diff --git a/src/main.h b/src/main.h index c1e925eb..a4c7d7fb 100644 --- a/src/main.h +++ b/src/main.h @@ -213,7 +213,7 @@ struct Client { struct Statusbar statusbar; void *comp; /* pointer to data used in completion.c */ Mode *mode; /* current active browser mode */ - WebKitWebContext *webctx; + /* WebKitWebContext *webctx; */ /* not used atm, use webkit_web_context_get_default() instead */ GtkWidget *window, *input; WebKitWebView *webview; guint64 page_id; /* page id of the webview */ diff --git a/src/setting.c b/src/setting.c index 373a9db3..d8b0de04 100644 --- a/src/setting.c +++ b/src/setting.c @@ -140,6 +140,7 @@ void setting_init(Client *c) setting_add(c, "fullscreen", TYPE_BOOLEAN, &off, fullscreen, 0, NULL); i = 100; setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); + setting_add(c, "download-path", TYPE_CHAR, &"~", NULL, 0, NULL); /* initialize the shortcuts and set the default shortcuts */ shortcut_init(c); From bc006a453301ccd27c2fba46d02388443d30ac5a Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 17 Oct 2016 22:35:59 +0700 Subject: [PATCH 028/201] Implements gui style settings --- README.md | 2 +- src/config.def.h | 14 +++----- src/main.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++- src/main.h | 1 + src/setting.c | 42 ++++++++++++++++++++++ 5 files changed, 138 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b46bc2f5..e22640d5 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ project directory. - [ ] 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 + - [x] 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 diff --git a/src/config.def.h b/src/config.def.h index cc3b915c..8d0f7f22 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -26,6 +26,8 @@ #define FEATURE_TITLE_PROGRESS /* show page title in url completions */ #define FEATURE_TITLE_IN_COMPLETION +/* support gui style settings compatible with vimb2 */ +#define FEATURE_GUI_STYLE_VIMB2_COMPAT #ifdef FEATURE_WGET_PROGRESS_BAR /* chars to use for the progressbar */ @@ -39,16 +41,8 @@ /* 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 "\ -#statusbar{color:#fff;background-color:#000;font:monospace bold 10;} \ -#statusbar.secure{background-color:#95e454;color:#000;} \ -#statusbar.insecure{background-color:#f77;color:#000;} \ -#input{background-color:#fff;color:#000;font:monospace 10;} \ -#input.error{background-color:#f77;font-weight:bold;} \ -#completion{color:#fff;background-color:#656565;font:monospace 10;} \ -#completion:hover{background-color:#777;} \ -#completion:selected{color:#f6f3e8;background-color:#888;}" +/* css applied to the gui elements regardless of user's settings */ +#define GUI_STYLE_CSS_BASE "#input text{background-color:inherit;color:inherit;caret-color:@color;font:inherit;}" /* default font size for fonts in webview */ #define SETTING_DEFAULT_FONT_SIZE 10 diff --git a/src/main.c b/src/main.c index 77d7af75..dcbed9cf 100644 --- a/src/main.c +++ b/src/main.c @@ -1312,7 +1312,96 @@ static void vimb_setup(void) /* Prepare the style provider to be used for the clients and completion. */ vb.style_provider = gtk_css_provider_get_default(); - gtk_css_provider_load_from_data(vb.style_provider, GUI_STYLE, -1, NULL); +} + +/** + * Update the gui style settings for client c, given a style setting name and a + * style setting value to be updated. The complete style sheet document will be + * regenerated and re-fed into gtk css provider. + */ +void vb_gui_style_update(Client *c, const char *setting_name_new, const char *setting_value_new) +{ + g_assert(c); + g_assert(setting_name_new); + g_assert(setting_value_new); + + /* The css style sheet document being composed in this function */ + GString *style_sheet = g_string_new(GUI_STYLE_CSS_BASE); + + /* Mapping from vimb config setting name to css style sheet string */ + static const char *setting_style_map[][2] = { +#ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT + {"completion-bg-active", " #completion:selected{background-color:%s;}"}, + {"completion-bg-normal", " #completion{background-color:%s;}"}, + {"completion-fg-active", " #completion:selected{color:%s;}"}, + {"completion-fg-normal", " #completion{color:%s;}"}, + {"completion-font", " #completion{font:%s;}"}, + {"input-bg-error", " #input.error{background-color:%s;}"}, + {"input-bg-normal", " #input{background-color:%s;}"}, + {"input-fg-error", " #input.error{color:%s;}"}, + {"input-fg-normal", " #input{color:%s;}"}, + {"input-font-error", " #input.error{font:%s;}"}, + {"input-font-normal", " #input{font:%s;}"}, + {"status-color-bg", " #statusbar{background-color:%s;}"}, + {"status-color-fg", " #statusbar{color:%s;}"}, + {"status-font", " #statusbar{font:%s;}"}, + {"status-ssl-color-bg", " #statusbar.secure{background-color:%s;}"}, + {"status-ssl-color-fg", " #statusbar.secure{color:%s;}"}, + {"status-ssl-font", " #statusbar.secure{font:%s;}"}, + {"status-sslinvalid-color-bg", " #statusbar.unsecure{background-color:%s;}"}, + {"status-sslinvalid-color-fg", " #statusbar.unsecure{color:%s;}"}, + {"status-sslinvalid-font", " #statusbar.unsecure{font:%s;}"}, +#else /* vimb3 gui style settings */ + {"completion-css", " #completion{%s;}"}, + {"completion-hover-css", " #completion:hover{%s;}"}, + {"completion-selected-css", " #completion:selected{%s;}"}, + {"input-css", " #input{%s;}"}, + {"input-error-css", " #input.error{%s;}"}, + {"status-css", " #statusbar{%s;}"}, + {"status-ssl-css", " #statusbar.secure{%s;}"}, + {"status-sslinvalid-css", " #statusbar.unsecure{%s;}"}, +#endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ + + {0, 0}, + }; + + /* For each supported style setting name */ + for (size_t i = 0; setting_style_map[i][0]; i++) { + const char *setting_name = setting_style_map[i][0]; + const char *style_string = setting_style_map[i][1]; + + /* If the current style setting name is the one to be updated, + * append the given value with appropriate css wrapping to the + * style sheet document. */ + if (strcmp(setting_name, setting_name_new) == 0) { + if (strlen(setting_value_new)) { + g_string_append_printf(style_sheet, style_string, setting_value_new); + } + } + /* If the current style setting name is NOT the one being updated, + * append the css string based on the current config setting. */ + else { + Setting* setting_value = (Setting*)g_hash_table_lookup(c->config.settings, setting_name); + + /* If the current style setting name is not available via settings + * yet - this happens during setting_init() - cleanup and return. + * We are going to be called again. With the last setting_add(), + * all style setting names are available. */ + if(!setting_value) { + goto cleanup; + } + + if (strlen(setting_value->value.s)) { + g_string_append_printf(style_sheet, style_string, setting_value->value.s); + } + } + } + + /* Feed style sheet document to gtk */ + gtk_css_provider_load_from_data(vb.style_provider, style_sheet->str, -1, NULL); + +cleanup: + g_string_free(style_sheet, TRUE); } /** diff --git a/src/main.h b/src/main.h index a4c7d7fb..9b93c6a2 100644 --- a/src/main.h +++ b/src/main.h @@ -275,5 +275,6 @@ 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); +void vb_gui_style_update(Client *c, const char *name, const char *value); #endif /* end of include guard: _MAIN_H */ diff --git a/src/setting.c b/src/setting.c index d8b0de04..6ebb3505 100644 --- a/src/setting.c +++ b/src/setting.c @@ -51,6 +51,7 @@ static void setting_free(Setting *s); static int cookie_accept(Client *c, const char *name, DataType type, void *value, void *data); static int default_zoom(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 gui_style(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); @@ -142,6 +143,40 @@ void setting_init(Client *c) setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); setting_add(c, "download-path", TYPE_CHAR, &"~", NULL, 0, NULL); +#ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT + /* gui style settings vimb2 compatibility */ + setting_add(c, "completion-bg-active", TYPE_CHAR, &"#888", gui_style, 0, NULL); + setting_add(c, "completion-bg-normal", TYPE_CHAR, &"#656565", gui_style, 0, NULL); + setting_add(c, "completion-fg-active", TYPE_CHAR, &"#f6f3e8", gui_style, 0, NULL); + setting_add(c, "completion-fg-normal", TYPE_CHAR, &"#fff", gui_style, 0, NULL); + setting_add(c, "completion-font", TYPE_CHAR, &"monospace 10", gui_style, 0, NULL); + setting_add(c, "input-bg-error", TYPE_CHAR, &"#f77", gui_style, 0, NULL); + setting_add(c, "input-bg-normal", TYPE_CHAR, &"#fff", gui_style, 0, NULL); + setting_add(c, "input-fg-error", TYPE_CHAR, &"#000", gui_style, 0, NULL); + setting_add(c, "input-fg-normal", TYPE_CHAR, &"#000", gui_style, 0, NULL); + setting_add(c, "input-font-error", TYPE_CHAR, &"monospace bold 10", gui_style, 0, NULL); + setting_add(c, "input-font-normal", TYPE_CHAR, &"monospace 10", gui_style, 0, NULL); + setting_add(c, "status-color-bg", TYPE_CHAR, &"#000", gui_style, 0, NULL); + setting_add(c, "status-color-fg", TYPE_CHAR, &"#fff", gui_style, 0, NULL); + setting_add(c, "status-font", TYPE_CHAR, &"monospace bold 10", gui_style, 0, NULL); + setting_add(c, "status-ssl-color-bg", TYPE_CHAR, &"#95e454", gui_style, 0, NULL); + setting_add(c, "status-ssl-color-fg", TYPE_CHAR, &"#000", gui_style, 0, NULL); + setting_add(c, "status-ssl-font", TYPE_CHAR, &"", gui_style, 0, NULL); + setting_add(c, "status-sslinvalid-color-bg", TYPE_CHAR, &"#f77", gui_style, 0, NULL); + setting_add(c, "status-sslinvalid-color-fg", TYPE_CHAR, &"#000", gui_style, 0, NULL); + setting_add(c, "status-sslinvalid-font", TYPE_CHAR, &"", gui_style, 0, NULL); +#else + /* gui style settings vimb3 */ + setting_add(c, "completion-css", TYPE_CHAR, &"color:#fff;background-color:#656565;font:monospace 10;", gui_style, 0, NULL); + setting_add(c, "completion-hover-css", TYPE_CHAR, &"background-color:#777;", gui_style, 0, NULL); + setting_add(c, "completion-selected-css", TYPE_CHAR, &"color:#f6f3e8;background-color:#888;", gui_style, 0, NULL); + setting_add(c, "input-css", TYPE_CHAR, &"background-color:#fff;color:#000;font:monospace 10;", gui_style, 0, NULL); + setting_add(c, "input-error-css", TYPE_CHAR, &"background-color:#f77;font-weight:bold;", gui_style, 0, NULL); + setting_add(c, "status-css", TYPE_CHAR, &"color:#fff;background-color:#000;font:monospace bold 10;", gui_style, 0, NULL); + setting_add(c, "status-ssl-css", TYPE_CHAR, &"background-color:#95e454;color:#000;", gui_style, 0, NULL); + setting_add(c, "status-sslinvalid-css", TYPE_CHAR, &"background-color:#f77;color:#000;", gui_style, 0, NULL); +#endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ + /* initialize the shortcuts and set the default shortcuts */ shortcut_init(c); shortcut_add(c, "dl", "https://duckduckgo.com/html/?q=$0"); @@ -613,6 +648,13 @@ static int statusbar(Client *c, const char *name, DataType type, void *value, vo return CMD_SUCCESS; } +static int gui_style(Client *c, const char *name, DataType type, void *value, void *data) +{ + vb_gui_style_update(c, name, (const char*)value); + + return CMD_SUCCESS; +} + static int tls_policy(Client *c, const char *name, DataType type, void *value, void *data) { gboolean strict = *((gboolean*)value); From 40183ae0aae819d757e7b45cecbe1cd7637e56db Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 18 Oct 2016 23:49:39 +0200 Subject: [PATCH 029/201] Removed none needed trailing ; in css rules. --- src/main.c | 56 +++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main.c b/src/main.c index dcbed9cf..12cbf307 100644 --- a/src/main.c +++ b/src/main.c @@ -1331,35 +1331,35 @@ void vb_gui_style_update(Client *c, const char *setting_name_new, const char *se /* Mapping from vimb config setting name to css style sheet string */ static const char *setting_style_map[][2] = { #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT - {"completion-bg-active", " #completion:selected{background-color:%s;}"}, - {"completion-bg-normal", " #completion{background-color:%s;}"}, - {"completion-fg-active", " #completion:selected{color:%s;}"}, - {"completion-fg-normal", " #completion{color:%s;}"}, - {"completion-font", " #completion{font:%s;}"}, - {"input-bg-error", " #input.error{background-color:%s;}"}, - {"input-bg-normal", " #input{background-color:%s;}"}, - {"input-fg-error", " #input.error{color:%s;}"}, - {"input-fg-normal", " #input{color:%s;}"}, - {"input-font-error", " #input.error{font:%s;}"}, - {"input-font-normal", " #input{font:%s;}"}, - {"status-color-bg", " #statusbar{background-color:%s;}"}, - {"status-color-fg", " #statusbar{color:%s;}"}, - {"status-font", " #statusbar{font:%s;}"}, - {"status-ssl-color-bg", " #statusbar.secure{background-color:%s;}"}, - {"status-ssl-color-fg", " #statusbar.secure{color:%s;}"}, - {"status-ssl-font", " #statusbar.secure{font:%s;}"}, - {"status-sslinvalid-color-bg", " #statusbar.unsecure{background-color:%s;}"}, - {"status-sslinvalid-color-fg", " #statusbar.unsecure{color:%s;}"}, - {"status-sslinvalid-font", " #statusbar.unsecure{font:%s;}"}, + {"completion-bg-active", " #completion:selected{background-color:%s}"}, + {"completion-bg-normal", " #completion{background-color:%s}"}, + {"completion-fg-active", " #completion:selected{color:%s}"}, + {"completion-fg-normal", " #completion{color:%s}"}, + {"completion-font", " #completion{font:%s}"}, + {"input-bg-error", " #input.error{background-color:%s}"}, + {"input-bg-normal", " #input{background-color:%s}"}, + {"input-fg-error", " #input.error{color:%s}"}, + {"input-fg-normal", " #input{color:%s}"}, + {"input-font-error", " #input.error{font:%s}"}, + {"input-font-normal", " #input{font:%s}"}, + {"status-color-bg", " #statusbar{background-color:%s}"}, + {"status-color-fg", " #statusbar{color:%s}"}, + {"status-font", " #statusbar{font:%s}"}, + {"status-ssl-color-bg", " #statusbar.secure{background-color:%s}"}, + {"status-ssl-color-fg", " #statusbar.secure{color:%s}"}, + {"status-ssl-font", " #statusbar.secure{font:%s}"}, + {"status-sslinvalid-color-bg", " #statusbar.unsecure{background-color:%s}"}, + {"status-sslinvalid-color-fg", " #statusbar.unsecure{color:%s}"}, + {"status-sslinvalid-font", " #statusbar.unsecure{font:%s}"}, #else /* vimb3 gui style settings */ - {"completion-css", " #completion{%s;}"}, - {"completion-hover-css", " #completion:hover{%s;}"}, - {"completion-selected-css", " #completion:selected{%s;}"}, - {"input-css", " #input{%s;}"}, - {"input-error-css", " #input.error{%s;}"}, - {"status-css", " #statusbar{%s;}"}, - {"status-ssl-css", " #statusbar.secure{%s;}"}, - {"status-sslinvalid-css", " #statusbar.unsecure{%s;}"}, + {"completion-css", " #completion{%s}"}, + {"completion-hover-css", " #completion:hover{%s}"}, + {"completion-selected-css", " #completion:selected{%s}"}, + {"input-css", " #input{%s}"}, + {"input-error-css", " #input.error{%s}"}, + {"status-css", " #statusbar{%s}"}, + {"status-ssl-css", " #statusbar.secure{%s}"}, + {"status-sslinvalid-css", " #statusbar.unsecure{%s}"}, #endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ {0, 0}, From 73465561834c13ab34db6d561d591e2cbc8880e3 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 00:07:55 +0200 Subject: [PATCH 030/201] Allow to set initial setting for gui fonts. There where multiple font configurations in the settings that have to be adapt by the user before compiling to setup some suitable default gui styling. So we use two new constants to setup a normal gui font and the emphasized one. --- src/config.def.h | 2 ++ src/setting.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index 8d0f7f22..d73d4806 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -46,4 +46,6 @@ /* default font size for fonts in webview */ #define SETTING_DEFAULT_FONT_SIZE 10 +#define SETTING_GUI_FONT_NORMAL "10px monospace" +#define SETTING_GUI_FONT_EMPH "bold 10px monospace" #define SETTING_HOME_PAGE "about:blank" diff --git a/src/setting.c b/src/setting.c index 6ebb3505..71eddad3 100644 --- a/src/setting.c +++ b/src/setting.c @@ -149,30 +149,30 @@ void setting_init(Client *c) setting_add(c, "completion-bg-normal", TYPE_CHAR, &"#656565", gui_style, 0, NULL); setting_add(c, "completion-fg-active", TYPE_CHAR, &"#f6f3e8", gui_style, 0, NULL); setting_add(c, "completion-fg-normal", TYPE_CHAR, &"#fff", gui_style, 0, NULL); - setting_add(c, "completion-font", TYPE_CHAR, &"monospace 10", gui_style, 0, NULL); + setting_add(c, "completion-font", TYPE_CHAR, &"" SETTING_GUI_FONT_NORMAL, gui_style, 0, NULL); setting_add(c, "input-bg-error", TYPE_CHAR, &"#f77", gui_style, 0, NULL); setting_add(c, "input-bg-normal", TYPE_CHAR, &"#fff", gui_style, 0, NULL); setting_add(c, "input-fg-error", TYPE_CHAR, &"#000", gui_style, 0, NULL); setting_add(c, "input-fg-normal", TYPE_CHAR, &"#000", gui_style, 0, NULL); - setting_add(c, "input-font-error", TYPE_CHAR, &"monospace bold 10", gui_style, 0, NULL); - setting_add(c, "input-font-normal", TYPE_CHAR, &"monospace 10", gui_style, 0, NULL); + setting_add(c, "input-font-error", TYPE_CHAR, &"" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); + setting_add(c, "input-font-normal", TYPE_CHAR, &"" SETTING_GUI_FONT_NORMAL, gui_style, 0, NULL); setting_add(c, "status-color-bg", TYPE_CHAR, &"#000", gui_style, 0, NULL); setting_add(c, "status-color-fg", TYPE_CHAR, &"#fff", gui_style, 0, NULL); - setting_add(c, "status-font", TYPE_CHAR, &"monospace bold 10", gui_style, 0, NULL); + setting_add(c, "status-font", TYPE_CHAR, &"" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); setting_add(c, "status-ssl-color-bg", TYPE_CHAR, &"#95e454", gui_style, 0, NULL); setting_add(c, "status-ssl-color-fg", TYPE_CHAR, &"#000", gui_style, 0, NULL); setting_add(c, "status-ssl-font", TYPE_CHAR, &"", gui_style, 0, NULL); setting_add(c, "status-sslinvalid-color-bg", TYPE_CHAR, &"#f77", gui_style, 0, NULL); setting_add(c, "status-sslinvalid-color-fg", TYPE_CHAR, &"#000", gui_style, 0, NULL); - setting_add(c, "status-sslinvalid-font", TYPE_CHAR, &"", gui_style, 0, NULL); + setting_add(c, "status-sslinvalid-font", TYPE_CHAR, &"" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); #else /* gui style settings vimb3 */ - setting_add(c, "completion-css", TYPE_CHAR, &"color:#fff;background-color:#656565;font:monospace 10;", gui_style, 0, NULL); + setting_add(c, "completion-css", TYPE_CHAR, &"color:#fff;background-color:#656565;font:" SETTING_GUI_FONT_NORMAL, gui_style, 0, NULL); setting_add(c, "completion-hover-css", TYPE_CHAR, &"background-color:#777;", gui_style, 0, NULL); setting_add(c, "completion-selected-css", TYPE_CHAR, &"color:#f6f3e8;background-color:#888;", gui_style, 0, NULL); - setting_add(c, "input-css", TYPE_CHAR, &"background-color:#fff;color:#000;font:monospace 10;", gui_style, 0, NULL); - setting_add(c, "input-error-css", TYPE_CHAR, &"background-color:#f77;font-weight:bold;", gui_style, 0, NULL); - setting_add(c, "status-css", TYPE_CHAR, &"color:#fff;background-color:#000;font:monospace bold 10;", gui_style, 0, NULL); + setting_add(c, "input-css", TYPE_CHAR, &"background-color:#fff;color:#000;font:" SETTING_GUI_FONT_NORMAL, gui_style, 0, NULL); + setting_add(c, "input-error-css", TYPE_CHAR, &"background-color:#f77;font:" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); + setting_add(c, "status-css", TYPE_CHAR, &"color:#fff;background-color:#000;font:" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); setting_add(c, "status-ssl-css", TYPE_CHAR, &"background-color:#95e454;color:#000;", gui_style, 0, NULL); setting_add(c, "status-sslinvalid-css", TYPE_CHAR, &"background-color:#f77;color:#000;", gui_style, 0, NULL); #endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ From 46c094a57f4b8b3b67909e1f390690306d65a577 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 00:26:02 +0200 Subject: [PATCH 031/201] Removed loop initial declarations. --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 12cbf307..0a158e80 100644 --- a/src/main.c +++ b/src/main.c @@ -1327,6 +1327,7 @@ void vb_gui_style_update(Client *c, const char *setting_name_new, const char *se /* The css style sheet document being composed in this function */ GString *style_sheet = g_string_new(GUI_STYLE_CSS_BASE); + size_t i; /* Mapping from vimb config setting name to css style sheet string */ static const char *setting_style_map[][2] = { @@ -1366,7 +1367,7 @@ void vb_gui_style_update(Client *c, const char *setting_name_new, const char *se }; /* For each supported style setting name */ - for (size_t i = 0; setting_style_map[i][0]; i++) { + for (i = 0; setting_style_map[i][0]; i++) { const char *setting_name = setting_style_map[i][0]; const char *style_string = setting_style_map[i][1]; From 2e99f769fb75d81e251efe1e2f8bcfca410f16c9 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 00:31:55 +0200 Subject: [PATCH 032/201] More code style hints. --- README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e22640d5..087a1390 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,41 @@ If you like to have a working vimb, please use the master branch. ## Patching and Coding style +### File Layout + +- Comment with LICENSE and possibly short explanation of file/tool +- Headers +- Macros +- Types +- Function declarations + - Include variable names + - For short files these can be left out + - Group/order in logical manner +- Global variables +- Function definitions in same order as declarations +- main + +### C Features + +- Do not mix declarations and code +- Do not use for loop initial declarations +- Use `/* */` for comments, not `//` + +### Headers + +- Place system/libc headers first in alphabetical order + - If headers must be included in a specific order comment to explain +- Place local headers after an empty line + +### Variables + +- Global variables not used outside translation unit should be declared static +- In declaration of pointers the `*` is adjacent to variable name, not type + +### Indentation + - 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` From 2cbadab4ceb262a0d0f741ce994e337f3a95fa99 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 22:07:19 +0200 Subject: [PATCH 033/201] Added manual page. --- Makefile | 6 +- doc/vimb.1 | 672 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 675 insertions(+), 3 deletions(-) create mode 100644 doc/vimb.1 diff --git a/Makefile b/Makefile index 912bf669..9cb1bf01 100644 --- a/Makefile +++ b/Makefile @@ -26,9 +26,9 @@ install: vimb install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET) @# man page install -d $(MANPREFIX)/man1 - # @sed -e "s!VERSION!$(VERSION)!g" \ - # -e "s!PREFIX!$(PREFIX)!g" \ - # -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(MANPREFIX)/man1/vimb.1 + @sed -e "s!VERSION!$(VERSION)!g" \ + -e "s!PREFIX!$(PREFIX)!g" \ + -e "s!DATE!`date +'%m %Y'`!g" $(DOCDIR)/vimb.1 > $(MANPREFIX)/man1/vimb.1 @# .desktop file install -d $(DOTDESKTOPPREFIX) install -m 644 vimb.desktop $(DOTDESKTOPPREFIX)/vimb.desktop diff --git a/doc/vimb.1 b/doc/vimb.1 new file mode 100644 index 00000000..bf780c60 --- /dev/null +++ b/doc/vimb.1 @@ -0,0 +1,672 @@ +.\" 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 +.PP +Mandatory arguments to long options are mandatory for short options too. +.TP +.BI "\-c, \-\-config " "FILE" +Use custom configuration given as \fIFILE\fP. +This will also be applied on new spawned instances. +.TP +.BI "\-e, \-\-embed " "WINID" +.I WINID +of an XEmbed-aware application, that Vimb will use as its parent. +.TP +.B "\-h, \-\-help" +Show help options. +.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 `` and `` 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 +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 +.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 +.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. +.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 +.TP +.B +Perform a click on element containing the current highlighted search result. +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 +.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 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 +cmap, cnoremap, imap, inoremap, nmap, nnoremap +.IP - +eval +.IP - +normal +.IP - +open, tabopen +.IP - +shellcmd +.PD +.SS Command Line Editing +.TP +.B , CTRL\-[, CTRL-C +Ignore all typed content and switch back to normal mode. +.TP +.B +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 prompt 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 +Start completion of the content in the inputbox in forward direction. +.TP +.B +Start completion of the content in the inputbox in backward direction. +.TP +.B +Step backward in the command history. +.TP +.B +Step forward in the command history. +Yank the current selection into register \fIx\fP and clipboard. +.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 "". +.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: , , , +for the cursor keys, , , , , , - and -. +.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 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 :set scripts=on:open !glib" +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" +Example which maps two spaces to go to 50% of the page. +.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 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 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_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 +.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. +.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 COMPLETIONS +The completions are triggered by pressing `` or `` 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" +will complete only URIs that contain the words foo and bar. +.PD +.RE +.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. +.SH FILES +.TP +.IR $XDG_CONFIG_HOME/vimb[/PROFILE] +Directory for configuration data. +If executed with \fB-p \fIPROFILE\fR parameter, configuration is read from +this subdirectory. +.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 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 +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 From 512ea79df8cf2d0e6473f4a4a980e427f7352a78 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 22:23:54 +0200 Subject: [PATCH 034/201] Show webview crash to the user. --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 0a158e80..e8c97dad 100644 --- a/src/main.c +++ b/src/main.c @@ -1139,7 +1139,8 @@ static void on_webview_ready_to_show(WebKitWebView *webview, Client *c) */ static gboolean on_webview_web_process_crashed(WebKitWebView *webview, Client *c) { - g_warning("Webview Crashed on %s", webkit_web_view_get_uri(webview)); + vb_echo(c, MSG_ERROR, FALSE, "Webview Crashed on %s", webkit_web_view_get_uri(webview)); + return TRUE; } From 4f7482e2f6c4263e0d0fd2116a2e2fa05a61f5e9 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 22:37:14 +0200 Subject: [PATCH 035/201] Shut up GTK warning on realize widget. We have to call gtk_widget_get_preferred_size to avoid warning like this when the window widget is realized. > Allocating size to WebKitWebViewBase 0x280c380 without calling > gtk_widget_get_preferred_width/height(). How does the code know the size > to allocate? --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index e8c97dad..d3588d79 100644 --- a/src/main.c +++ b/src/main.c @@ -514,8 +514,13 @@ static Client *client_new(WebKitWebView *webview) c->window = gtk_plug_new(vb.embed); xid = g_strdup_printf("%d", (int)vb.embed); } else { + GtkRequisition req; c->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_role(GTK_WINDOW(c->window), PROJECT_UCFIRST); + /* We have to call gtk_widget_get_preferred_size before + * gtk_widget_size_allocate otherwise a warning is thrown when the + * widget is realized. */ + gtk_widget_get_preferred_size(GTK_WIDGET(c->window), &req, NULL); gtk_widget_realize(GTK_WIDGET(c->window)); xid = g_strdup_printf("%d", From f1e576ef069ddcccf4913d68b7ca390688d6a91d Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 19 Oct 2016 23:14:52 +0200 Subject: [PATCH 036/201] Update usage agent string to current webkit version. --- src/setting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index 71eddad3..4a6a41bd 100644 --- a/src/setting.c +++ b/src/setting.c @@ -74,7 +74,7 @@ void setting_init(Client *c) * 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"); + setting_add(c, "useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/603.1 (KHTML, like Gecko) " PROJECT "/" VERSION " Version/10.0 Safari/603.1", 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"); From 3cb9d54c96219ba68837a6da68c57f6b7c03bbf5 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 22 Oct 2016 23:30:24 +0200 Subject: [PATCH 037/201] Attempt to start dbus server from ui. --- Makefile | 9 +- README.md | 1 + src/ext-proxy.c | 185 +++++++++++++-------- src/ext-proxy.h | 2 +- src/main.c | 11 +- src/main.h | 1 + src/webextension/ext-main.c | 317 +++++++++++++++++++++--------------- 7 files changed, 309 insertions(+), 217 deletions(-) diff --git a/Makefile b/Makefile index 9cb1bf01..a554a39e 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,11 @@ all: vimb options: @echo "vimb build options:" - @echo "LIBS = $(LIBS)" - @echo "CFLAGS = $(CFLAGS)" - @echo "LDFLAGS = $(LDFLAGS)" - @echo "CC = $(CC)" + @echo "LIBS = $(LIBS)" + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "EXTCFLAGS = $(EXTCFLAGS)" + @echo "CC = $(CC)" vimb: $(SUBDIRS:%=%.subdir-all) diff --git a/README.md b/README.md index 087a1390..3b4d73fa 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ project directory. this is now available with the `RUNPREFIX` option for the make - [x] establish communication channel between the vimb instance and the now required webextension (dbus) + - [ ] use propper authorization for dbus peers 2. migrate as many of the features of the webkit1 vimb - [x] starting with custom config file `--config,-c` option - [ ] running multiple ex-commands during startup `--cmd,-C` options diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 0fe07b8c..2e7fb5b2 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -24,16 +24,18 @@ #include "main.h" #include "webextension/ext-main.h" +static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, + GIOStream *stream, GCredentials *credentials, gpointer data); +static gboolean on_new_connection(GDBusServer *server, + GDBusConnection *connection, gpointer data); +static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, + gpointer data); 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, @@ -43,34 +45,125 @@ static void on_web_extension_page_created(GDBusConnection *connection, * vimb may hold multiple clients which may use more than one webprocess and * therefore multiple webextension instances. */ extern struct Vimb vb; +static GDBusServer *dbusserver; /** - * Request the web extension to focus first editable element. - * Returns whether an focusable element was found or not. + * Initialize the dbus proxy by watching for appearing dbus name. */ -void ext_proxy_focus_input(Client *c) +const char *ext_proxy_init(void) { - dbus_call(c, "FocusInput", NULL, NULL); + char *address, *guid; + GDBusAuthObserver *observer; + GError *error = NULL; + + address = g_strdup_printf("unix:tmpdir=%s", g_get_tmp_dir()); + guid = g_dbus_generate_guid(); + observer = g_dbus_auth_observer_new(); + + g_signal_connect(observer, "authorize-authenticated-peer", + G_CALLBACK(on_authorize_authenticated_peer), NULL); + + /* Use sync call because server must be starte before the web extension + * attempt to connect */ + dbusserver = g_dbus_server_new_sync(address, G_DBUS_SERVER_FLAGS_NONE, + guid, observer, NULL, &error); + + if (error) { + g_warning("Failed to start web extension server on %s: %s", address, error->message); + g_error_free(error); + goto out; + } + + g_signal_connect(dbusserver, "new-connection", G_CALLBACK(on_new_connection), NULL); + g_dbus_server_start(dbusserver); + +out: + g_free(address); + g_free(guid); + g_object_unref(observer); + + return g_dbus_server_get_client_address(dbusserver); } -/** - * Initialize the dbus proxy by watching for appearing dbus name. - */ -void ext_proxy_init(const char *id) +/* TODO move this to a lib or somthing that can be used from ui and web + * process together */ +static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, + GIOStream *stream, GCredentials *credentials, gpointer data) { - 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, + static GCredentials *own_credentials = NULL; + GError *error = NULL; + + if (!own_credentials) { + own_credentials = g_credentials_new(); + } + + if (credentials && g_credentials_is_same_user(credentials, own_credentials, &error)) { + return TRUE; + } + + if (error) { + g_warning("Failed to authorize web extension connection: %s", error->message); + g_error_free(error); + } + + return FALSE; +} + +static gboolean on_new_connection(GDBusServer *server, + GDBusConnection *connection, gpointer data) +{ + /* Create dbus proxy. */ + g_return_val_if_fail(G_IS_DBUS_CONNECTION(connection), FALSE); + + /*g_signal_connect(connection, "closed", G_CALLBACK(connection_closed_cb), NULL);*/ + + g_dbus_proxy_new(connection, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, NULL, + VB_WEBEXTENSION_OBJECT_PATH, + VB_WEBEXTENSION_INTERFACE, NULL, + (GAsyncReadyCallback)on_proxy_created, NULL); - g_free(service_name); + + return TRUE; +} + +static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, + gpointer data) +{ + GError *error = NULL; + GDBusProxy *proxy; + GDBusConnection *connection; + + proxy = g_dbus_proxy_new_finish(result, &error); + if (!proxy) { + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_warning("Error creating web extension proxy: %s", error->message); + } + g_error_free(error); + + /* TODO cancel the dbus connection - use cancelable */ + return; + } + + connection = g_dbus_proxy_get_connection(proxy); + 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); +} + +/** + * 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); } /** @@ -90,6 +183,7 @@ static void dbus_call(Client *c, const char *method, GVariant *param, /* TODO add function to queue calls until the proxy connection is * established */ if (!c->dbusproxy) { + g_message("dbus_call without proxy: %s", method); return; } g_dbus_proxy_call(c->dbusproxy, method, param, G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, c); @@ -116,53 +210,6 @@ static void on_editable_change_focus(GDBusConnection *connection, /* 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. @@ -200,7 +247,7 @@ static void on_web_extension_page_created(GDBusConnection *connection, if (p) { /* Set the dbus proxy on the right client based on page id. */ - p->dbusproxy = data; + p->dbusproxy = (GDBusProxy*)data; g_dbus_connection_signal_subscribe(connection, NULL, VB_WEBEXTENSION_INTERFACE, "VerticalScroll", diff --git a/src/ext-proxy.h b/src/ext-proxy.h index 3d6ae141..8258df9d 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -22,8 +22,8 @@ #include "main.h" +const char *ext_proxy_init(void); 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/main.c b/src/main.c index d3588d79..f7187ce3 100644 --- a/src/main.c +++ b/src/main.c @@ -754,20 +754,15 @@ static void on_webctx_download_started(WebKitWebContext *webctx, */ static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data) { - char *name; - static guint ext_count = 0; + const char *name; GVariant *vdata; /* Setup the extension directory. */ webkit_web_context_set_web_extensions_directory(webctx, EXTPREFIX); - name = g_strdup_printf("%u-%u", getpid(), ++ext_count); - ext_proxy_init(name); - - vdata = g_variant_new("(s)", name); + name = ext_proxy_init(); + vdata = g_variant_new("(ms)", name); webkit_web_context_set_web_extensions_initialization_user_data(webctx, vdata); - - g_free(name); } /** diff --git a/src/main.h b/src/main.h index 9b93c6a2..212cd38e 100644 --- a/src/main.h +++ b/src/main.h @@ -219,6 +219,7 @@ struct Client { guint64 page_id; /* page id of the webview */ GtkTextBuffer *buffer; GDBusProxy *dbusproxy; + GDBusServer *dbusserver; struct { /* TODO split in global setting definitions and set values on a per * client base. */ diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index a3a8a838..cfc8e01e 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -27,19 +27,26 @@ #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 gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, + GIOStream *stream, GCredentials *credentials, gpointer extension); +static void on_dbus_connection_created(GObject *source_object, + GAsyncResult *result, gpointer data); +static void add_onload_event_observers(WebKitDOMDocument *doc, + WebKitWebExtension *extension); +static void emit_page_created(GDBusConnection *connection, guint64 pageid); +static void emit_page_created_pending(GDBusConnection *connection); +static void queue_page_created_signal(guint64 pageid); +static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, + 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 void on_editable_change_focus(WebKitDOMEventTarget *target, + WebKitDOMEvent *event, WebKitWebExtension *extension); +static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data); +static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension); +static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, + WebKitURIResponse *response, gpointer extension); static const GDBusInterfaceVTable interface_vtable = { dbus_handle_method_call, @@ -73,6 +80,7 @@ struct Ext { GHashTable *headers; GHashTable *documents; gboolean input_focus; + GArray *page_created_signals; }; struct Ext ext = {0}; @@ -83,32 +91,79 @@ struct Ext ext = {0}; 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); + char *server_address; + GDBusAuthObserver *observer; - /* 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); + g_variant_get(data, "(ms)", &server_address); + if (!server_address) { + g_warning("UI process did not start D-Bus server"); + return; + } + + g_signal_connect(extension, "page-created", G_CALLBACK(on_page_created), NULL); + + observer = g_dbus_auth_observer_new(); + g_signal_connect(observer, "authorize-authenticated-peer", + G_CALLBACK(on_authorize_authenticated_peer), extension); + + g_dbus_connection_new_for_address(server_address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, observer, NULL, + (GAsyncReadyCallback)on_dbus_connection_created, extension); + g_object_unref(observer); +} + +static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, + GIOStream *stream, GCredentials *credentials, gpointer extension) +{ + /* FIXME require authentication logic */ + return TRUE; +} - service_name = g_strdup_printf("%s-%s", VB_WEBEXTENSION_SERVICE_NAME, extid); +static void on_dbus_connection_created(GObject *source_object, + GAsyncResult *result, gpointer data) +{ + static GDBusNodeInfo *node_info = NULL; + GDBusConnection *connection; + GError *error = NULL; - /* 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); + if (!node_info) { + node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL); } - g_free(service_name); + connection = g_dbus_connection_new_for_address_finish(result, &error); + if (error) { + g_warning("Failed to connect to UI process: %s", error->message); + g_error_free(error); + return; + } - g_signal_connect(extension, "page-created", G_CALLBACK(on_page_created), NULL); + /* register the webextension object */ + 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); + g_object_unref(connection); + return; + } + + emit_page_created_pending(connection); + ext.connection = connection; } /** * Add observers to doc event for given document and all the contained iframes * too. */ -static void add_onload_event_observers(WebKitDOMDocument *doc) +static void add_onload_event_observers(WebKitDOMDocument *doc, + WebKitWebExtension *extension) { WebKitDOMEventTarget *target; @@ -117,20 +172,72 @@ static void add_onload_event_observers(WebKitDOMDocument *doc) 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)); + 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); + G_CALLBACK(on_editable_change_focus), TRUE, extension); webkit_dom_event_target_add_event_listener(target, "blur", - G_CALLBACK(on_editable_change_focus), TRUE, NULL); + G_CALLBACK(on_editable_change_focus), TRUE, extension); /* 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); + on_editable_change_focus(target, NULL, extension); +} + +/** + * Emit the page created signal that is used in the ui process to finish the + * dbus proxy connection. + */ +static void emit_page_created(GDBusConnection *connection, guint64 pageid) +{ + GError *error = NULL; + + /* propagate the signal over dbus */ + g_dbus_connection_emit_signal(G_DBUS_CONNECTION(connection), NULL, + VB_WEBEXTENSION_OBJECT_PATH, VB_WEBEXTENSION_INTERFACE, + "PageCreated", g_variant_new("(t)", pageid), &error); + + if (error) { + g_warning("Failed to emit signal PageCreated: %s", error->message); + g_error_free(error); + } +} + +/** + * Emit queued page created signals. + */ +static void emit_page_created_pending(GDBusConnection *connection) +{ + int i; + guint64 pageid; + + if (!ext.page_created_signals) { + return; + } + + for (i = 0; i < ext.page_created_signals->len; i++) { + pageid = g_array_index(ext.page_created_signals, guint64, i); + emit_page_created(connection, pageid); + } + + g_array_free(ext.page_created_signals, TRUE); + ext.page_created_signals = NULL; +} + +/** + * Write the page id of the created page to a queue to send them to the ui + * process when the dbus connection is established. + */ +static void queue_page_created_signal(guint64 pageid) +{ + if (!ext.page_created_signals) { + ext.page_created_signals = g_array_new(FALSE, FALSE, sizeof(guint64)); + } + ext.page_created_signals = g_array_append_val(ext.page_created_signals, pageid); } /** @@ -139,11 +246,11 @@ static void add_onload_event_observers(WebKitDOMDocument *doc) * @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) +static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, + GVariant *data) { GError *error = NULL; - /* Don't do anythings if the dbus connection was not established. */ if (!ext.connection) { return; } @@ -152,8 +259,6 @@ static void dbus_emit_signal(const char *name, GVariant *data) 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); @@ -165,7 +270,7 @@ 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) + GVariant *parameters, GDBusMethodInvocation *invocation, gpointer extension) { char *value; @@ -184,84 +289,13 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, } } -/** - * 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) +static void on_editable_change_focus(WebKitDOMEventTarget *target, + WebKitDOMEvent *event, WebKitWebExtension *extension) { gboolean input_focus; WebKitDOMDocument *doc; @@ -285,7 +319,7 @@ static void on_editable_change_focus(WebKitDOMEventTarget *target, WebKitDOMEven iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active); subdoc = webkit_dom_html_iframe_element_get_content_document(iframe); - add_onload_event_observers(subdoc); + add_onload_event_observers(subdoc, extension); return; } @@ -296,30 +330,36 @@ static void on_editable_change_focus(WebKitDOMEventTarget *target, WebKitDOMEven if (input_focus != ext.input_focus) { ext.input_focus = input_focus; - dbus_emit_signal("EditableChangeFocus", g_variant_new("(b)", input_focus)); + dbus_emit_signal("EditableChangeFocus", extension, + g_variant_new("(b)", input_focus)); } } /** * Callback for web extensions page-created signal. */ -static void on_page_created(WebKitWebExtension *extension, WebKitWebPage *page, gpointer data) +static void on_page_created(WebKitWebExtension *extension, WebKitWebPage *webpage, gpointer data) { + guint64 pageid = webkit_web_page_get_id(webpage); + /* Save the new created page in the extension data for later use. */ - ext.webpage = page; + ext.webpage = webpage; + if (ext.connection) { + emit_page_created(ext.connection, pageid); + } else { + queue_page_created_signal(pageid); + } - 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, + g_object_connect(webpage, + "signal::send-request", G_CALLBACK(on_web_page_send_request), extension, + "signal::document-loaded", G_CALLBACK(on_web_page_document_loaded), extension, 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) +static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension) { /* If there is a hashtable of known document - detroy this and create a * new hashtable. */ @@ -328,31 +368,38 @@ static void on_web_page_document_loaded(WebKitWebPage *page, gpointer data) } ext.documents = g_hash_table_new(g_direct_hash, g_direct_equal); - add_onload_event_observers(webkit_web_page_get_dom_document(page)); + add_onload_event_observers(webkit_web_page_get_dom_document(webpage), + WEBKIT_WEB_EXTENSION(extension)); } /** * Callback for web pages send-request signal. */ -static gboolean on_web_page_send_request(WebKitWebPage *page, WebKitURIRequest *request, - WebKitURIResponse *response, gpointer data) +static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, + WebKitURIResponse *response, gpointer extension) { + char *name, *value; + SoupMessageHeaders *headers; + GHashTableIter iter; + + if (!ext.headers) { + return FALSE; + } + /* 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); - } + headers = webkit_uri_request_get_http_headers(request); + if (!headers) { + return FALSE; + } + + 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); } } From fa619c8886e78ac0108217f9884ed4ba1ad25a52 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 31 Jan 2017 23:37:57 +0100 Subject: [PATCH 038/201] Set minimum webkit version to support to 2.3.5. This version allowed to initialize the webextension with user data which is essential to setup the dbus connection. --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 2bf9eed8..c445f5ad 100644 --- a/config.mk +++ b/config.mk @@ -14,7 +14,7 @@ DOCDIR = doc SUBDIRS = $(SRCDIR)/scripts $(SRCDIR)/webextension $(SRCDIR) # used libs -LIBS = gtk+-3.0 webkit2gtk-4.0 +LIBS = gtk+-3.0 'webkit2gtk-4.0 >= 2.3.5' # setup general used CFLAGS CFLAGS += -std=c99 -pipe -Wall From 9ead350f88b734b55002e92e887a7821bb951660 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 31 Jan 2017 23:39:37 +0100 Subject: [PATCH 039/201] Show linker action during make. --- src/Makefile | 2 +- src/scripts/Makefile | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e4e2faea..ddf3d153 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,7 +10,7 @@ clean: vimb: $(OBJ) @echo "$(CC) $@" - @$(CC) $(LDFLAGS) $(OBJ) -o $@ + $(CC) $(LDFLAGS) $(OBJ) -o $@ $(OBJ): config.h $(BASEDIR)/config.mk diff --git a/src/scripts/Makefile b/src/scripts/Makefile index 32af1912..93f242d6 100644 --- a/src/scripts/Makefile +++ b/src/scripts/Makefile @@ -10,7 +10,6 @@ clean: scripts.h: $(JSFILES) @echo "create $@ from *.js" - @echo > $@ @for file in $(JSFILES); do \ ./js2h.sh $$file >> $@; \ done From a825ba97de3af8d315b79717dad4cdafe9ab5d00 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 1 Feb 2017 00:33:22 +0100 Subject: [PATCH 040/201] Added real auth observer to webextension too. --- src/Makefile | 3 +-- src/webextension/ext-main.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index ddf3d153..b1bd8be9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,8 +9,7 @@ clean: $(RM) vimb *.o vimb: $(OBJ) - @echo "$(CC) $@" - $(CC) $(LDFLAGS) $(OBJ) -o $@ + $(CC) $(LDFLAGS) $^ -o $@ $(OBJ): config.h $(BASEDIR)/config.mk diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index cfc8e01e..f2d5d751 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -115,8 +115,23 @@ void webkit_web_extension_initialize_with_user_data(WebKitWebExtension *extensio static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, GIOStream *stream, GCredentials *credentials, gpointer extension) { - /* FIXME require authentication logic */ - return TRUE; + static GCredentials *own_credentials = NULL; + GError *error = NULL; + + if (!own_credentials) { + own_credentials = g_credentials_new(); + } + + if (credentials && g_credentials_is_same_user(credentials, own_credentials, &error)) { + return TRUE; + } + + if (error) { + g_warning("Failed to authorize connection to ui: %s", error->message); + g_error_free(error); + } + + return FALSE; } static void on_dbus_connection_created(GObject *source_object, @@ -126,6 +141,7 @@ static void on_dbus_connection_created(GObject *source_object, GDBusConnection *connection; GError *error = NULL; + g_message("on_dbus_connection_created"); if (!node_info) { node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL); } From cf94439378b6bcaead3d4d25c529f52449742977 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 1 Feb 2017 00:46:57 +0100 Subject: [PATCH 041/201] Removed debug message. --- src/webextension/ext-main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index f2d5d751..ae1fe72c 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -141,7 +141,6 @@ static void on_dbus_connection_created(GObject *source_object, GDBusConnection *connection; GError *error = NULL; - g_message("on_dbus_connection_created"); if (!node_info) { node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL); } From 1d405d6e20b5bf0a67bec5270d02123c3dfd2e94 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 1 Feb 2017 00:57:02 +0100 Subject: [PATCH 042/201] Add new task to README. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b4d73fa..ca560715 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,9 @@ project directory. this is now available with the `RUNPREFIX` option for the make - [x] establish communication channel between the vimb instance and the now required webextension (dbus) - - [ ] use propper authorization for dbus peers + - [x] use propper authorization for dbus peers + - [ ] queue settings that needs to be pushed to the webextension until the + dbus connection is established. 2. migrate as many of the features of the webkit1 vimb - [x] starting with custom config file `--config,-c` option - [ ] running multiple ex-commands during startup `--cmd,-C` options From b4a2118a7b27a6502afee957dde089b84ca26427 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 17 Feb 2017 23:42:18 +0100 Subject: [PATCH 043/201] makes font sizes match webkit2 defaults --- src/config.def.h | 3 ++- src/setting.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index d73d4806..1f55e240 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -45,7 +45,8 @@ #define GUI_STYLE_CSS_BASE "#input text{background-color:inherit;color:inherit;caret-color:@color;font:inherit;}" /* default font size for fonts in webview */ -#define SETTING_DEFAULT_FONT_SIZE 10 +#define SETTING_DEFAULT_FONT_SIZE 16 +#define SETTING_DEFAULT_MONOSPACE_FONT_SIZE 13 #define SETTING_GUI_FONT_NORMAL "10px monospace" #define SETTING_GUI_FONT_EMPH "bold 10px monospace" #define SETTING_HOME_PAGE "about:blank" diff --git a/src/setting.c b/src/setting.c index 4a6a41bd..ef23eb75 100644 --- a/src/setting.c +++ b/src/setting.c @@ -102,7 +102,7 @@ void setting_init(Client *c) i = 5; 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; + i = SETTING_DEFAULT_MONOSPACE_FONT_SIZE; 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"); From 248147537c0306ca0209956a079795772322d2e2 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 17 Feb 2017 23:43:36 +0100 Subject: [PATCH 044/201] changes default gui font to 10pt --- src/config.def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index 1f55e240..8b2d27b8 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -47,6 +47,6 @@ /* default font size for fonts in webview */ #define SETTING_DEFAULT_FONT_SIZE 16 #define SETTING_DEFAULT_MONOSPACE_FONT_SIZE 13 -#define SETTING_GUI_FONT_NORMAL "10px monospace" -#define SETTING_GUI_FONT_EMPH "bold 10px monospace" -#define SETTING_HOME_PAGE "about:blank" +#define SETTING_GUI_FONT_NORMAL "10pt monospace" +#define SETTING_GUI_FONT_EMPH "bold 10pt monospace" +#define SETTING_HOME_PAGE "about:blank" From aca5b9b088285244ab0258c30246844a4164230f Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 20 Feb 2017 23:49:57 +0100 Subject: [PATCH 045/201] implements ex_shortcut() --- src/ex.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/ex.c b/src/ex.c index d1d38c5a..e9b1c641 100644 --- a/src/ex.c +++ b/src/ex.c @@ -35,6 +35,7 @@ #include "main.h" #include "map.h" #include "setting.h" +#include "shortcut.h" #include "util.h" typedef enum { @@ -966,8 +967,38 @@ static VbCmdResult ex_handlers(Client *c, const ExArg *arg) static VbCmdResult ex_shortcut(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ - return CMD_SUCCESS; + char *key, *uri; + gboolean res = CMD_ERROR; + + if (!c) { + return CMD_ERROR; + } + + if (!arg || !arg->name) { + return CMD_ERROR; + } + + if (!arg->rhs || !arg->rhs->str || !*arg->rhs->str) { + return CMD_ERROR; + } + + if (strstr(arg->name, "shortcut-add")) { + if ((uri = strchr(arg->rhs->str, '='))) { + key = arg->rhs->str; + *uri++ = '\0'; /* devide key and uri */ + g_strstrip(key); + g_strstrip(uri); + res = shortcut_add(c, key, uri); + } + } else if (strstr(arg->name, "shortcut-remove")) { + g_strstrip(arg->rhs->str); + res = shortcut_remove(c, arg->rhs->str); + } else if (strstr(arg->name, "shortcut-default")) { + g_strstrip(arg->rhs->str); + res = shortcut_set_default(c, arg->rhs->str); + } + + return res; } /** From bfe8ad4672d81fe2c247e4210b5448a70579ffe5 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 23 Feb 2017 10:46:21 +0100 Subject: [PATCH 046/201] improves ex_shortcut() inspired by master branch --- src/ex.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/ex.c b/src/ex.c index e9b1c641..7f779507 100644 --- a/src/ex.c +++ b/src/ex.c @@ -967,38 +967,42 @@ static VbCmdResult ex_handlers(Client *c, const ExArg *arg) static VbCmdResult ex_shortcut(Client *c, const ExArg *arg) { - char *key, *uri; - gboolean res = CMD_ERROR; + gchar *uri; + gboolean success = false; if (!c) { return CMD_ERROR; } - if (!arg || !arg->name) { - return CMD_ERROR; - } - if (!arg->rhs || !arg->rhs->str || !*arg->rhs->str) { return CMD_ERROR; } - if (strstr(arg->name, "shortcut-add")) { - if ((uri = strchr(arg->rhs->str, '='))) { - key = arg->rhs->str; - *uri++ = '\0'; /* devide key and uri */ - g_strstrip(key); - g_strstrip(uri); - res = shortcut_add(c, key, uri); - } - } else if (strstr(arg->name, "shortcut-remove")) { - g_strstrip(arg->rhs->str); - res = shortcut_remove(c, arg->rhs->str); - } else if (strstr(arg->name, "shortcut-default")) { - g_strstrip(arg->rhs->str); - res = shortcut_set_default(c, arg->rhs->str); + switch(arg->code) { + case EX_SCA: + if ((uri = strchr(arg->rhs->str, '='))) { + *uri++ = '\0'; /* devide key and uri */ + g_strstrip(arg->rhs->str); + g_strstrip(uri); + success = shortcut_add(c, arg->rhs->str, uri); + } + break; + + case EX_SCR: + g_strstrip(arg->rhs->str); + success = shortcut_remove(c, arg->rhs->str); + break; + + case EX_SCD: + g_strstrip(arg->rhs->str); + success = shortcut_set_default(c, arg->rhs->str); + break; + + default: + break; } - return res; + return success ? CMD_SUCCESS : CMD_ERROR; } /** From 495727fb19cc83289fa4ba90c91590d28ea7e740 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 26 Feb 2017 00:29:12 +0100 Subject: [PATCH 047/201] fixes (shift)tab cycling through editable elements --- src/input.c | 3 ++- src/normal.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/input.c b/src/input.c index afa93606..6236c9d7 100644 --- a/src/input.c +++ b/src/input.c @@ -38,6 +38,7 @@ void input_enter(Client *c) * disturbing the user */ gtk_widget_grab_focus(GTK_WIDGET(c->webview)); vb_modelabel_update(c, "-- INPUT --"); + webkit_web_view_run_javascript(c->webview, "var vimb_input_mode_element = document.activeElement;", NULL, NULL, NULL); } /** @@ -45,7 +46,7 @@ void input_enter(Client *c) */ void input_leave(Client *c) { - webkit_web_view_run_javascript(c->webview, "document.activeElement.blur();", NULL, NULL, NULL); + webkit_web_view_run_javascript(c->webview, "vimb_input_mode_element.blur();", NULL, NULL, NULL); vb_modelabel_update(c, ""); } diff --git a/src/normal.c b/src/normal.c index 7410fb9e..e92de75c 100644 --- a/src/normal.c +++ b/src/normal.c @@ -215,7 +215,6 @@ extern struct Vimb vb; */ void normal_enter(Client *c) { - 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(c->webview)); @@ -322,6 +321,7 @@ void pass_enter(Client *c) */ void pass_leave(Client *c) { + webkit_web_view_run_javascript(c->webview, "document.activeElement.blur();", NULL, NULL, NULL); vb_modelabel_update(c, ""); } From fdb7d1fda3e34590c86579a635da1deafed32307 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 27 Feb 2017 22:58:24 +0100 Subject: [PATCH 048/201] Don't sort url history completion list. Also made sorting the default be cause it's less code. --- src/ex.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ex.c b/src/ex.c index 7f779507..c3acc0af 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1093,31 +1093,27 @@ static gboolean complete(Client *c, short direction) case EX_OPEN: case EX_TABOPEN: /* TODO add bookmark completion if *token == '!' */ + sort = FALSE; found = history_fill_completion(store, HISTORY_URL, token); break; case EX_SET: - sort = TRUE; found = setting_fill_completion(c, store, token); break; case EX_BMA: - sort = TRUE; /* TODO fill bookmark completion */ break; case EX_SCR: - sort = TRUE; /* TODO fill shortcut completion */ break; case EX_HANDREM: - sort = TRUE; /* TODO fill handler completion */ break; case EX_SAVE: - sort = TRUE; found = util_filename_fill_completion(c, store, token); break; @@ -1136,7 +1132,6 @@ static gboolean complete(Client *c, short direction) if (ex_fill_completion(store, in)) { OVERWRITE_STRING(excomp.prefix, ":"); found = TRUE; - sort = FALSE; } } free_cmdarg(arg); @@ -1145,6 +1140,7 @@ static gboolean complete(Client *c, short direction) OVERWRITE_STRING(excomp.token, in + 1); OVERWRITE_NSTRING(excomp.prefix, in, 1); found = TRUE; + sort = FALSE; } } From 2a50746f179af391f6a74c474eecadeed2300e80 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 27 Feb 2017 23:13:40 +0100 Subject: [PATCH 049/201] changes enable-tabs-to-links setting name for consistency --- src/setting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index ef23eb75..e45dea3e 100644 --- a/src/setting.c +++ b/src/setting.c @@ -116,7 +116,7 @@ void setting_init(Client *c) setting_add(c, "smooth-scrolling", TYPE_BOOLEAN, &off, webkit, 0, "enable-smooth-scrolling"); #endif 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, "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"); From 8b6a89dc070dd33c85eade752eb492bbae6bce89 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 27 Feb 2017 23:26:37 +0100 Subject: [PATCH 050/201] adds ctrl+p and ctrl+n for ex history movement --- src/ex.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ex.c b/src/ex.c index 7f779507..65126fa1 100644 --- a/src/ex.c +++ b/src/ex.c @@ -262,11 +262,13 @@ VbResult ex_keypress(Client *c, int key) complete(c, -1); break; - case KEY_UP: + case KEY_UP: /* fall through */ + case CTRL('P'): history(c, TRUE); break; - case KEY_DOWN: + case KEY_DOWN: /* fall through */ + case CTRL('N'): history(c, FALSE); break; From ca8dfe7dee5f4c7474f0723ec65cf9a3978f083e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 28 Feb 2017 00:09:19 +0100 Subject: [PATCH 051/201] Allow to manage bookmarks and queue. --- README.md | 12 +- src/bookmark.c | 329 +++++++++++++++++++++++++++++++++++++++++++++++ src/bookmark.h | 37 ++++++ src/command.c | 45 ++++++- src/config.def.h | 2 + src/ex.c | 59 ++++++++- src/normal.c | 4 +- src/util.c | 101 +++++++++++++++ src/util.h | 3 + 9 files changed, 578 insertions(+), 14 deletions(-) create mode 100644 src/bookmark.c create mode 100644 src/bookmark.h diff --git a/README.md b/README.md index ca560715..2eee22ea 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,21 @@ project directory. - [ ] hinting - [x] searching and matching of search results - [x] navigation j, k, h, l, ... - - [ ] history and history lookup + - [x] history and history lookup - [ ] completion + - [ ] augroup + - [ ] autocmd + - [x] bookmarks + - [ ] file paths for :source and :save + - [x] search phrases + - [x] settings + - [ ] url handler + - [x] url history - [ ] HSTS - [ ] auto-response-header - [x] cookies support - [x] register support and `:register` command - - [ ] read it later queue + - [x] read it later queue - [ ] show scroll indicator in statusbar as top, x%, bottom or all how can we get this information from webview easily? - [x] find a way to disable the scrollbars on the main frame diff --git a/src/bookmark.c b/src/bookmark.c new file mode 100644 index 00000000..110d72d5 --- /dev/null +++ b/src/bookmark.c @@ -0,0 +1,329 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 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 + +#include "config.h" +#include "main.h" +#include "bookmark.h" +#include "util.h" +#include "completion.h" + +typedef struct { + char *uri; + char *title; + char *tags; +} Bookmark; + +extern struct Vimb vb; + +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 titletags */ + 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 new file mode 100644 index 00000000..dea45568 --- /dev/null +++ b/src/bookmark.h @@ -0,0 +1,37 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 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 + +#include "main.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 */ + diff --git a/src/command.c b/src/command.c index 700bd2ce..7fbc8785 100644 --- a/src/command.c +++ b/src/command.c @@ -21,11 +21,15 @@ * This file contains functions that are used by normal mode and command mode * together. */ -#include "command.h" +#include + #include "config.h" +#ifdef FEATURE_QUEUE +#include "bookmark.h" +#endif +#include "command.h" #include "history.h" #include "main.h" -#include extern struct Vimb vb; @@ -164,7 +168,40 @@ gboolean command_save(Client *c, const Arg *arg) #ifdef FEATURE_QUEUE gboolean command_queue(Client *c, const Arg *arg) { - /* TODO no implemented yet */ - return TRUE; + 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(c, &(Arg){TARGET_CURRENT, uri}); + g_free(uri); + } + vb_echo(c, MSG_NORMAL, FALSE, "Queue length %d", count); + break; + + case COMMAND_QUEUE_PUSH: + res = bookmark_queue_push(arg->s ? arg->s : c->state.uri); + if (res) { + vb_echo(c, MSG_NORMAL, FALSE, "Pushed to queue"); + } + break; + + case COMMAND_QUEUE_UNSHIFT: + res = bookmark_queue_unshift(arg->s ? arg->s : c->state.uri); + if (res) { + vb_echo(c, MSG_NORMAL, FALSE, "Pushed to queue"); + } + break; + + case COMMAND_QUEUE_CLEAR: + if (bookmark_queue_clear()) { + vb_echo(c, MSG_NORMAL, FALSE, "Queue cleared"); + } + break; + } + + return res; } #endif diff --git a/src/config.def.h b/src/config.def.h index 8b2d27b8..37874b7f 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -28,6 +28,8 @@ #define FEATURE_TITLE_IN_COMPLETION /* support gui style settings compatible with vimb2 */ #define FEATURE_GUI_STYLE_VIMB2_COMPAT +/* enable the read it later queue */ +#define FEATURE_QUEUE #ifdef FEATURE_WGET_PROGRESS_BAR /* chars to use for the progressbar */ diff --git a/src/ex.c b/src/ex.c index c3acc0af..4ca91530 100644 --- a/src/ex.c +++ b/src/ex.c @@ -27,6 +27,7 @@ #include #include "ascii.h" +#include "bookmark.h" #include "command.h" #include "completion.h" #include "config.h" @@ -745,8 +746,19 @@ static void free_cmdarg(ExArg *arg) static VbCmdResult ex_bookmark(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ - return CMD_SUCCESS; + if (arg->code == EX_BMR) { + if (bookmark_remove(*arg->rhs->str ? arg->rhs->str : c->state.uri)) { + vb_echo_force(c, MSG_NORMAL, TRUE, " Bookmark removed"); + + return CMD_SUCCESS | CMD_KEEPINPUT; + } + } else if (bookmark_add(c->state.uri, c->state.title, arg->rhs->str)) { + vb_echo_force(c, MSG_NORMAL, TRUE, " Bookmark added"); + + return CMD_SUCCESS | CMD_KEEPINPUT; + } + + return CMD_ERROR; } static VbCmdResult ex_eval(Client *c, const ExArg *arg) @@ -855,8 +867,38 @@ static VbCmdResult ex_open(Client *c, const ExArg *arg) #ifdef FEATURE_QUEUE static VbCmdResult ex_queue(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ - return CMD_SUCCESS; + 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 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(c, &a) + ? CMD_SUCCESS | CMD_KEEPINPUT + : CMD_ERROR | CMD_KEEPINPUT; } #endif @@ -1092,9 +1134,12 @@ static gboolean complete(Client *c, short direction) switch (arg->code) { case EX_OPEN: case EX_TABOPEN: - /* TODO add bookmark completion if *token == '!' */ sort = FALSE; - found = history_fill_completion(store, HISTORY_URL, token); + if (*token == '!') { + found = bookmark_fill_completion(store, token + 1); + } else { + found = history_fill_completion(store, HISTORY_URL, token); + } break; case EX_SET: @@ -1102,7 +1147,7 @@ static gboolean complete(Client *c, short direction) break; case EX_BMA: - /* TODO fill bookmark completion */ + found = bookmark_fill_tag_completion(store, token); break; case EX_SCR: diff --git a/src/normal.c b/src/normal.c index e92de75c..faa57331 100644 --- a/src/normal.c +++ b/src/normal.c @@ -633,7 +633,9 @@ static VbResult normal_prevnext(Client *c, const NormalCmdInfo *info) static VbResult normal_queue(Client *c, const NormalCmdInfo *info) { - /* TODO run next uri from queu and remove it from */ +#ifdef FEATURE_QUEUE + command_queue(c, &((Arg){COMMAND_QUEUE_POP})); +#endif return RESULT_COMPLETE; } diff --git a/src/util.c b/src/util.c index be48e711..29557f2b 100644 --- a/src/util.c +++ b/src/util.c @@ -104,6 +104,79 @@ gboolean util_file_append(const char *file, const char *format, ...) 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 *new, + *line = NULL; + int len, + count = 0; + + if (lines) { + 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; + 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; +} + /** * Retrieves the config directory path. * Returnes string must be freed. @@ -259,6 +332,34 @@ GList *util_file_to_unique_list(const char *filename, Util_Content_Func func, return gl; } +/** + * 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; + } + } + } + + return found; +} + /** * Fills file path completion entries into given list store for also given * input. diff --git a/src/util.h b/src/util.h index 94a60ca7..ca9ba68c 100644 --- a/src/util.h +++ b/src/util.h @@ -33,12 +33,15 @@ typedef void *(*Util_Content_Func)(const char*, const char*); 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_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_fill_completion(GtkListStore *store, const char *input, GList *src); 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); From 9bac99eb3a6f818aee67ef37573a7fdc2a73661d Mon Sep 17 00:00:00 2001 From: Joachim Desroches Date: Tue, 28 Feb 2017 16:38:58 +0100 Subject: [PATCH 052/201] Add :source command back. --- src/ex.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ex.c b/src/ex.c index 5e1afcea..42438cef 100644 --- a/src/ex.c +++ b/src/ex.c @@ -72,6 +72,7 @@ typedef enum { EX_SCR, EX_SET, EX_SHELLCMD, + EX_SOURCE, EX_TABOPEN, } ExCode; @@ -140,6 +141,7 @@ 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_source(Client *c, const ExArg *arg); static VbCmdResult ex_handlers(Client *c, const ExArg *arg); static gboolean complete(Client *c, short direction); @@ -186,6 +188,7 @@ 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}, }; @@ -1049,6 +1052,11 @@ static VbCmdResult ex_shortcut(Client *c, const ExArg *arg) return success ? CMD_SUCCESS : CMD_ERROR; } +static VbCmdResult ex_source(Client *c, const ExArg *arg) +{ + return ex_run_file(c, arg->rhs->str); +} + /** * Manage the generation and stepping through completions. * This function prepared some prefix and suffix string that are required to @@ -1160,7 +1168,8 @@ static gboolean complete(Client *c, short direction) /* TODO fill handler completion */ break; - case EX_SAVE: + case EX_SAVE: /* Fallthrough */ + case EX_SOURCE: found = util_filename_fill_completion(c, store, token); break; From 650561c71a7cd8a19d6a231e5c31e993b061ada8 Mon Sep 17 00:00:00 2001 From: Joachim Desroches Date: Tue, 28 Feb 2017 16:39:38 +0100 Subject: [PATCH 053/201] Add documentation for the :source command back. --- doc/vimb.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index bf780c60..e3b9dee6 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -517,6 +517,9 @@ 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. From 13b0f1b3b43a609177c7822da6c3f713de52ec0f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 1 Mar 2017 23:24:16 +0100 Subject: [PATCH 054/201] Marked file path completion as done. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2eee22ea..f04c8603 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ project directory. - [ ] augroup - [ ] autocmd - [x] bookmarks - - [ ] file paths for :source and :save + - [x] file paths for :source and :save - [x] search phrases - [x] settings - [ ] url handler From 32052a688b646ab4a18c7fb7987c1f1025257d54 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 00:01:42 +0100 Subject: [PATCH 055/201] Cleanup settings on quit. --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index f7187ce3..904bf19a 100644 --- a/src/main.c +++ b/src/main.c @@ -485,6 +485,7 @@ static void client_destroy(Client *c) completion_cleanup(c); map_cleanup(c); register_cleanup(c); + setting_cleanup(c); g_slice_free(Client, c); From af741e150987c1e5ce04a0c6c875b7e629b3b48a Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 00:54:22 +0100 Subject: [PATCH 056/201] Added url file handlers. Allow to set protocol handlers via :handler-add that process url protocol like mailto: by using a specific tool. --- README.md | 4 +-- doc/vimb.1 | 22 ++++++++++++ src/ex.c | 25 +++++++++++-- src/handler.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/handler.h | 31 ++++++++++++++++ src/main.c | 32 +++++++++++------ src/main.h | 3 ++ 7 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 src/handler.c create mode 100644 src/handler.h diff --git a/README.md b/README.md index f04c8603..e9494210 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ project directory. - [x] file paths for :source and :save - [x] search phrases - [x] settings - - [ ] url handler + - [x] url handler - [x] url history - [ ] HSTS - [ ] auto-response-header @@ -131,7 +131,7 @@ project directory. - [x] default zoom - [x] yanking - [x] keymapping - - [ ] URL handler + - [x] URL handler - [x] shortcuts - [ ] autocommands and augroups 3. documentation diff --git a/doc/vimb.1 b/doc/vimb.1 index e3b9dee6..92f64798 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -396,6 +396,28 @@ Save the current opened URI with \fItags\fP to the bookmark file. .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 mailto=urxvt -e mutt %s" +to start email client for mailto links. +.IP ":handler-add magnet=xdg-open %s" +to open magnet links with xdg-open. +.IP ":handler-add ftp=urxvt -e wget %s -P ~/ftp-downloads" +to handle ftp downloads via wget. +.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. diff --git a/src/ex.c b/src/ex.c index 42438cef..eaa97cb1 100644 --- a/src/ex.c +++ b/src/ex.c @@ -32,6 +32,7 @@ #include "completion.h" #include "config.h" #include "ex.h" +#include "handler.h" #include "history.h" #include "main.h" #include "map.h" @@ -1008,8 +1009,26 @@ static VbCmdResult ex_shellcmd(Client *c, const ExArg *arg) static VbCmdResult ex_handlers(Client *c, const ExArg *arg) { - /* TODO no implemented yet */ - return CMD_SUCCESS; + char *p; + gboolean res = FALSE; + + switch (arg->code) { + case EX_HANDADD: + if (arg->rhs->len && (p = strchr(arg->rhs->str, '='))) { + *p++ = '\0'; + res = handler_add(c, arg->rhs->str, p); + } + break; + + case EX_HANDREM: + res = handler_remove(c, arg->rhs->str); + break; + + default: + break; + } + + return res ? CMD_SUCCESS : CMD_ERROR; } static VbCmdResult ex_shortcut(Client *c, const ExArg *arg) @@ -1165,7 +1184,7 @@ static gboolean complete(Client *c, short direction) break; case EX_HANDREM: - /* TODO fill handler completion */ + found = handler_fill_completion(c, store, token); break; case EX_SAVE: /* Fallthrough */ diff --git a/src/handler.c b/src/handler.c new file mode 100644 index 00000000..6cde8888 --- /dev/null +++ b/src/handler.c @@ -0,0 +1,99 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 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 + +#include "main.h" +#include "handler.h" +#include "util.h" + +extern struct Vimb vb; + +static char *handler_lookup(Client *c, const char *uri); + +void handler_init(Client *c) +{ + c->handlers.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +void handler_cleanup(Client *c) +{ + if (c->handlers.table) { + g_hash_table_destroy(c->handlers.table); + c->handlers.table = NULL; + } +} + +gboolean handler_add(Client *c, const char *key, const char *cmd) +{ + g_hash_table_insert(c->handlers.table, g_strdup(key), g_strdup(cmd)); + + return true; +} + +gboolean handler_remove(Client *c, const char *key) +{ + return g_hash_table_remove(c->handlers.table, key); +} + +gboolean handler_handle_uri(Client *c, const char *uri) +{ + char *handler, *cmd; + GError *error = NULL; + gboolean res; + + if (!(handler = handler_lookup(c, 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); + res = FALSE; + } else { + res = TRUE; + } + + g_free(cmd); + return res; +} + +gboolean handler_fill_completion(Client *c, GtkListStore *store, const char *input) +{ + GList *src = g_hash_table_get_keys(c->handlers.table); + gboolean found = util_fill_completion(store, input, src); + g_list_free(src); + + return found; +} + +static char *handler_lookup(Client *c, const char *uri) +{ + char *p, *schema, *handler = NULL; + + if ((p = strchr(uri, ':'))) { + schema = g_strndup(uri, p - uri); + handler = g_hash_table_lookup(c->handlers.table, schema); + g_free(schema); + } + + return handler; +} + diff --git a/src/handler.h b/src/handler.h new file mode 100644 index 00000000..ce16bdf4 --- /dev/null +++ b/src/handler.h @@ -0,0 +1,31 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 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 handler_init(Client *c); +void handler_cleanup(Client *c); +gboolean handler_add(Client *c, const char *key, const char *cmd); +gboolean handler_remove(Client *c, const char *key); +gboolean handler_handle_uri(Client *c, const char *uri); +gboolean handler_fill_completion(Client *c, GtkListStore *store, const char *input); + +#endif /* end of include guard: _HANDLERS_H */ + diff --git a/src/main.c b/src/main.c index 904bf19a..53fbc154 100644 --- a/src/main.c +++ b/src/main.c @@ -32,6 +32,7 @@ #include "config.h" #include "ex.h" #include "ext-proxy.h" +#include "handler.h" #include "input.h" #include "js.h" #include "main.h" @@ -486,6 +487,7 @@ static void client_destroy(Client *c) map_cleanup(c); register_cleanup(c); setting_cleanup(c); + handler_cleanup(c); g_slice_free(Client, c); @@ -586,6 +588,9 @@ static Client *client_new(WebKitWebView *webview) /* initialize the settings */ setting_init(c); + /* initialize the url handlers */ + handler_init(c); + /* start client in normal mode */ vb_enter(c, 'n'); @@ -956,24 +961,29 @@ static WebKitWebView *on_webview_create(WebKitWebView *webview, static gboolean on_webview_decide_policy(WebKitWebView *webview, WebKitPolicyDecision *dec, WebKitPolicyDecisionType type, Client *c) { - guint status; + guint status, button, mod; 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); + a = webkit_navigation_policy_decision_get_navigation_action(WEBKIT_NAVIGATION_POLICY_DECISION(dec)); + req = webkit_navigation_action_get_request(a); + button = webkit_navigation_action_get_mouse_button(a); + mod = webkit_navigation_action_get_modifiers(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; - } + /* Try to handle with specific protocol handler. */ + if (handler_handle_uri(c, webkit_uri_request_get_uri(req))) { + webkit_policy_decision_ignore(dec); + return TRUE; + } + if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED + && (button == 2 || (button == 1 && mod & GDK_CONTROL_MASK))) { + + webkit_policy_decision_ignore(dec); + spawn_new_instance(webkit_uri_request_get_uri(req), TRUE); + return TRUE; } return FALSE; diff --git a/src/main.h b/src/main.h index 212cd38e..59e1fbbc 100644 --- a/src/main.h +++ b/src/main.h @@ -244,6 +244,9 @@ struct Client { GHashTable *table; char *fallback; /* default shortcut to use if none given in request */ } shortcut; + struct { + GHashTable *table; /* holds the protocol handlers */ + } handlers; }; struct Vimb { From 5bd0cba16eade607ff6298e2ff8ab6511defca7c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 01:10:12 +0100 Subject: [PATCH 057/201] Added completion for shortcuts. Completion for shortcuts are now available for :shortcut-remove and :shortcut-default. --- src/ex.c | 5 +++-- src/shortcut.c | 4 +--- src/shortcut.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ex.c b/src/ex.c index eaa97cb1..cc4bdbe9 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1179,8 +1179,9 @@ static gboolean complete(Client *c, short direction) found = bookmark_fill_tag_completion(store, token); break; - case EX_SCR: - /* TODO fill shortcut completion */ + case EX_SCR: /* Fallthrough */ + case EX_SCD: + found = shortcut_fill_completion(c, store, token); break; case EX_HANDREM: diff --git a/src/shortcut.c b/src/shortcut.c index 7545037e..af858ab0 100644 --- a/src/shortcut.c +++ b/src/shortcut.c @@ -157,8 +157,7 @@ char *shortcut_get_uri(Client *c, const char *string) return uri; } -#if 0 -gboolean shortcut_fill_completion(GtkListStore *store, const char *input) +gboolean shortcut_fill_completion(Client *c, GtkListStore *store, const char *input) { GList *src = g_hash_table_get_keys(c->shortcut.table); gboolean found = util_fill_completion(store, input, src); @@ -166,7 +165,6 @@ gboolean shortcut_fill_completion(GtkListStore *store, const char *input) return found; } -#endif /** * Retrieves th highest placeholder number used in given string. diff --git a/src/shortcut.h b/src/shortcut.h index 9b44e29f..21e21ff1 100644 --- a/src/shortcut.h +++ b/src/shortcut.h @@ -26,7 +26,7 @@ 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);*/ +gboolean shortcut_fill_completion(Client *c, GtkListStore *store, const char *input); #endif /* end of include guard: _SHORTCUT_H */ From 9e050c62475775d53d6d3ded39dee66e72f9eb68 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 21:33:41 +0100 Subject: [PATCH 058/201] Added manual test pages for focus/blur issues. --- tests/manual/editable-focus-in-iframe.html | 9 +++++ tests/manual/editable-focus.html | 41 ++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/manual/editable-focus-in-iframe.html create mode 100644 tests/manual/editable-focus.html diff --git a/tests/manual/editable-focus-in-iframe.html b/tests/manual/editable-focus-in-iframe.html new file mode 100644 index 00000000..46fffe0b --- /dev/null +++ b/tests/manual/editable-focus-in-iframe.html @@ -0,0 +1,9 @@ + + +Track Focu/Blur also within iFrames + + + + + + diff --git a/tests/manual/editable-focus.html b/tests/manual/editable-focus.html new file mode 100644 index 00000000..e3cc0f55 --- /dev/null +++ b/tests/manual/editable-focus.html @@ -0,0 +1,41 @@ + + +Input mode Switching + + + +

+ Run with scripts=on and strict-focus=off +

    +
  1. If page is loade, vimb should be in input mode.
  2. +
  3. Set strict-focus=on and reload page. Vimb should keep + in normal mode
  4. +
  5. Independent from the strict-focus should vimb switch + to input mode if the button is clicked
  6. +
+

+
+
+
+
+
+ +
+
+

+ Also the following element using contenteditable="true" + should switch vimb into input mode on click. +

+
+ Clicking this element using contenteditable="true" should + switch vimb into input mode too. +
+ + + From 94f67dc4468f3d1151d5174b8af9d5c5bcc760a8 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 21:40:56 +0100 Subject: [PATCH 059/201] Simplified test page javascript. --- tests/manual/editable-focus.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/editable-focus.html b/tests/manual/editable-focus.html index e3cc0f55..5c0d123c 100644 --- a/tests/manual/editable-focus.html +++ b/tests/manual/editable-focus.html @@ -24,7 +24,7 @@


-
+
From 1b42616b5ed0a09d84120e892a1073cf3cd7cb5d Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 2 Mar 2017 23:48:12 +0100 Subject: [PATCH 060/201] Marked embed as done. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9494210..7e47a947 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ project directory. - [x] 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 + - [x] 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` From 4860da82a0cbacd40c3bd251724397343190e703 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 2 Mar 2017 10:06:54 +0100 Subject: [PATCH 061/201] allows shortcuts without parameters --- src/shortcut.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shortcut.c b/src/shortcut.c index af858ab0..4e83e4b8 100644 --- a/src/shortcut.c +++ b/src/shortcut.c @@ -202,6 +202,8 @@ static const char *shortcut_lookup(Client *c, const char *string, const char **q *query = p + 1; } g_free(key); + } else { + uri = g_hash_table_lookup(c->shortcut.table, string); } if (!uri && c->shortcut.fallback From bd0de523a883e9ab66535ba0e48a9caa1f2d7efb Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 3 Mar 2017 12:16:48 +0100 Subject: [PATCH 062/201] A little bit more silence. --- src/ext-proxy.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 2e7fb5b2..b17f0814 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -183,7 +183,6 @@ static void dbus_call(Client *c, const char *method, GVariant *param, /* TODO add function to queue calls until the proxy connection is * established */ if (!c->dbusproxy) { - g_message("dbus_call without proxy: %s", method); return; } g_dbus_proxy_call(c->dbusproxy, method, param, G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, c); From 910649ab8b9d16bd9ed363ed7ebbb4549234103c Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Wed, 8 Mar 2017 21:51:06 +0100 Subject: [PATCH 063/201] fixes prompt color after error --- src/normal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/normal.c b/src/normal.c index faa57331..e71a4f16 100644 --- a/src/normal.c +++ b/src/normal.c @@ -506,7 +506,8 @@ static VbResult normal_increment_decrement(Client *c, const NormalCmdInfo *info) static VbResult normal_input_open(Client *c, const NormalCmdInfo *info) { if (strchr("ot", info->key)) { - vb_input_set_text(c, info->key == 't' ? ":tabopen " : ":open "); + vb_echo(c, MSG_NORMAL, FALSE, + ":%s ", info->key == 't' ? "tabopen" : "open"); } else { vb_echo(c, MSG_NORMAL, FALSE, ":%s %s", info->key == 'T' ? "tabopen" : "open", c->state.uri); From 0731d27b02b16a42f9ebef92bee20770ec82c403 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 10 Mar 2017 22:52:11 +0100 Subject: [PATCH 064/201] Don't show webview if it's not ready to be shown. --- src/main.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 53fbc154..4da48c43 100644 --- a/src/main.c +++ b/src/main.c @@ -44,7 +44,7 @@ #include "util.h" static void client_destroy(Client *c); -static Client *client_new(WebKitWebView *webview); +static Client *client_new(WebKitWebView *webview, gboolean); static gboolean input_clear(Client *c); static void input_print(Client *c, gboolean force, MessageType type, gboolean hide, const char *message); @@ -268,7 +268,7 @@ gboolean vb_load_uri(Client *c, const Arg *arg) } else if (arg->i == TARGET_NEW) { spawn_new_instance(uri, TRUE); } else { /* TARGET_RELATET */ - Client *newclient = client_new(c->webview); + Client *newclient = client_new(c->webview, FALSE); /* Load the uri into the new client. */ webkit_web_view_load_uri(newclient->webview, uri); set_title(c, uri); @@ -503,7 +503,7 @@ static void client_destroy(Client *c) * @webview: Related webview or NULL if a client with an independent * webview shoudl be created. */ -static Client *client_new(WebKitWebView *webview) +static Client *client_new(WebKitWebView *webview, gboolean show) { Client *c; char *xid; @@ -596,7 +596,9 @@ static Client *client_new(WebKitWebView *webview) c->state.enable_register = TRUE; - gtk_widget_show_all(c->window); + if (show) { + gtk_widget_show_all(c->window); + } /* read the config file */ ex_run_file(c, vb.files[FILES_CONFIG]); @@ -948,7 +950,7 @@ static void on_webview_close(WebKitWebView *webview, Client *c) static WebKitWebView *on_webview_create(WebKitWebView *webview, WebKitNavigationAction *navact, Client *c) { - Client *new = client_new(webview); + Client *new = client_new(webview, FALSE); return new->webview; } @@ -1142,7 +1144,7 @@ static void on_webview_notify_uri(WebKitWebView *webview, GParamSpec *pspec, Cli */ static void on_webview_ready_to_show(WebKitWebView *webview, Client *c) { - gtk_widget_show(GTK_WIDGET(webview)); + gtk_widget_show_all(GTK_WIDGET(c->window)); } /** @@ -1513,7 +1515,7 @@ int main(int argc, char* argv[]) vb.embed = strtol(winid, NULL, 0); } - c = client_new(NULL); + c = client_new(NULL, TRUE); if (argc <= 1) { vb_load_uri(c, &(Arg){TARGET_CURRENT, NULL}); } else { From 03a0629231379353f09488fe89669a597230a46f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 10 Mar 2017 23:06:33 +0100 Subject: [PATCH 065/201] Remove webkit version check. This is already covered by the requirements. --- src/setting.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/setting.c b/src/setting.c index e45dea3e..59bf50a9 100644 --- a/src/setting.c +++ b/src/setting.c @@ -112,9 +112,7 @@ void setting_init(Client *c) 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(c, "smooth-scrolling", TYPE_BOOLEAN, &off, webkit, 0, "enable-smooth-scrolling"); -#endif setting_add(c, "spacial-navigation", TYPE_BOOLEAN, &off, webkit, 0, "enable-spatial-navigation"); setting_add(c, "tabs-to-links", TYPE_BOOLEAN, &on, webkit, 0, "enable-tabs-to-links"); setting_add(c, "webaudio", TYPE_BOOLEAN, &off, webkit, 0, "enable-webaudio"); From d4a2b4e27c886c541094a9b28c2ca896cbc60979 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 23 Mar 2017 13:28:00 +0100 Subject: [PATCH 066/201] Call subdir make only for one level #331. We can't control dependencies in case all the subdirs are processed from the upper most Makefile. So now the subdir make used to create the webextension and the script is called from the src/Makefile which knows somethings more about dependencies. --- Makefile | 18 +++++++----------- config.mk | 1 - src/Makefile | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index a554a39e..e2af9b93 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -all: vimb +all: $(SRCDIR)/vimb options: @echo "vimb build options:" @@ -10,15 +10,10 @@ options: @echo "EXTCFLAGS = $(EXTCFLAGS)" @echo "CC = $(CC)" -vimb: $(SUBDIRS:%=%.subdir-all) +$(SRCDIR)/vimb: + @$(MAKE) $(MFLAGS) -C $(SRCDIR) -%.subdir-all: - @$(MAKE) $(MFLAGS) -C $* - -%.subdir-clean: - @$(MAKE) $(MFLAGS) -C $* clean - -install: vimb +install: $(SRCDIR)/vimb @# binary install -d $(BINPREFIX) install -m 755 $(SRCDIR)/vimb $(BINPREFIX)/vimb @@ -37,7 +32,8 @@ install: vimb uninstall: $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) -clean: $(SUBDIRS:%=%.subdir-clean) +clean: + @$(MAKE) $(MFLAGS) -C $(SRCDIR) clean sandbox: @make $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install @@ -45,4 +41,4 @@ sandbox: runsandbox: sandbox sandbox/usr/bin/vimb -.PHONY: all options clean install uninstall sandbox sandbox-clean +.PHONY: all vimb options clean install uninstall sandbox diff --git a/config.mk b/config.mk index c445f5ad..93cd476d 100644 --- a/config.mk +++ b/config.mk @@ -11,7 +11,6 @@ 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 >= 2.3.5' diff --git a/src/Makefile b/src/Makefile index b1bd8be9..d85bd266 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,17 +1,18 @@ BASEDIR=.. include $(BASEDIR)/config.mk -OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) +SUBDIRS = scripts webextension +OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) all: vimb -clean: +clean: $(SUBDIRS:%=%.subdir-clean) $(RM) vimb *.o -vimb: $(OBJ) - $(CC) $(LDFLAGS) $^ -o $@ +vimb: $(OBJ) webextension.subdir-all + $(CC) $(LDFLAGS) $(OBJ) -o $@ -$(OBJ): config.h $(BASEDIR)/config.mk +$(OBJ): scripts.subdir-all config.h $(BASEDIR)/config.mk -include $(OBJ:.o=.d) @@ -23,4 +24,10 @@ config.h: @echo "${CC} $@" @$(CC) $(CFLAGS) -c -o $@ $< +%.subdir-all: config.h + @$(MAKE) $(MFLAGS) -C $* + +%.subdir-clean: + @$(MAKE) $(MFLAGS) -C $* clean + .PHONY: all clean From 586b7df329d091660723f98b84fe9065420944be Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 23 Mar 2017 21:23:42 +0100 Subject: [PATCH 067/201] Mentioned changed font style syntax in changelog. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fb2ea46..68621092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## [unreleased] +### Changed + +* Syntax for the font related gui settings has be changed. + Fonts have to be given as `[ font-style | font-variant | font-weight | font-stretch ]? font-size font-family` + Example `set input-font-normal=bold 10pt "DejaVu Sans Mono"` instead of + previous `set input-fg-normal=DejaVu Sans Mono Bold 10` + ### Removed * `FEATURE_COOKIE` precompiler flag was removed bcause compiling without cookie From f948e8cafc33d98060206d040847e9007f7d5f1f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 23 Mar 2017 21:30:16 +0100 Subject: [PATCH 068/201] Don't use compat mode by default. --- src/config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.def.h b/src/config.def.h index 37874b7f..999a6602 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -27,7 +27,7 @@ /* show page title in url completions */ #define FEATURE_TITLE_IN_COMPLETION /* support gui style settings compatible with vimb2 */ -#define FEATURE_GUI_STYLE_VIMB2_COMPAT +/* #define FEATURE_GUI_STYLE_VIMB2_COMPAT */ /* enable the read it later queue */ #define FEATURE_QUEUE From 342b2585b6f93fffce51045fe382d59aa9c382a7 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 00:31:50 +0200 Subject: [PATCH 069/201] use %.subdir-{all,clean} targets in root makefile - improves consistency: this mechanism is used in other places as well - fixes dependency tracking: root makefile does not check files directly but delegates the task to sub makefiles --- Makefile | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index e2af9b93..63a43939 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -all: $(SRCDIR)/vimb +all: $(SRCDIR).subdir-all options: @echo "vimb build options:" @@ -10,10 +10,7 @@ options: @echo "EXTCFLAGS = $(EXTCFLAGS)" @echo "CC = $(CC)" -$(SRCDIR)/vimb: - @$(MAKE) $(MFLAGS) -C $(SRCDIR) - -install: $(SRCDIR)/vimb +install: $(SRCDIR).subdir-all @# binary install -d $(BINPREFIX) install -m 755 $(SRCDIR)/vimb $(BINPREFIX)/vimb @@ -32,8 +29,7 @@ install: $(SRCDIR)/vimb uninstall: $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) -clean: - @$(MAKE) $(MFLAGS) -C $(SRCDIR) clean +clean: $(SRCDIR).subdir-clean sandbox: @make $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install @@ -41,4 +37,10 @@ sandbox: runsandbox: sandbox sandbox/usr/bin/vimb -.PHONY: all vimb options clean install uninstall sandbox +%.subdir-all: + @$(MAKE) $(MFLAGS) -C $* + +%.subdir-clean: + @$(MAKE) $(MFLAGS) -C $* clean + +.PHONY: all options install uninstall clean sandbox runsandbox From 7a2a79f81d870faa3d1bb31978febe0822d08496 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 00:34:23 +0200 Subject: [PATCH 070/201] allow directory names in js2h parameters strips directory names from js2h js file parameters. this allows calling js2h from directories other than one the js source resides in --- src/scripts/js2h.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index b1770851..82903d55 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -13,9 +13,9 @@ if [ ! -r "$FILE" ]; then 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) +# Remove the path and .js file extension and turn all chars to upper case to +# get the constant name. +CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:.js$::g' | tr a-z A-Z) # minify the script cat $FILE | \ From e1faf06e1dfde64407ac66dc78e51c3bd83d63dd Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 00:38:58 +0200 Subject: [PATCH 071/201] moves scripts.h make target to src/Makefile this allows better dependency tracking like regenerate scripts.h only if a js file changed and only rebuild objects if scripts.h changed --- src/Makefile | 13 ++++++++++--- src/scripts/Makefile | 18 ------------------ 2 files changed, 10 insertions(+), 21 deletions(-) delete mode 100644 src/scripts/Makefile diff --git a/src/Makefile b/src/Makefile index d85bd266..5ec16d82 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,18 +1,19 @@ BASEDIR=.. include $(BASEDIR)/config.mk -SUBDIRS = scripts webextension +SUBDIRS = webextension OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) +JSFILES = $(wildcard scripts/*.js) all: vimb clean: $(SUBDIRS:%=%.subdir-clean) - $(RM) vimb *.o + $(RM) vimb *.o scripts/scripts.h vimb: $(OBJ) webextension.subdir-all $(CC) $(LDFLAGS) $(OBJ) -o $@ -$(OBJ): scripts.subdir-all config.h $(BASEDIR)/config.mk +$(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h -include $(OBJ:.o=.d) @@ -20,6 +21,12 @@ config.h: @echo create $@ from config.def.h @cp config.def.h $@ +scripts/scripts.h: $(JSFILES) + @echo "create $@ from *.js" + @for file in $(JSFILES); do \ + ./scripts/js2h.sh $$file >> $@; \ + done + %.o: %.c @echo "${CC} $@" @$(CC) $(CFLAGS) -c -o $@ $< diff --git a/src/scripts/Makefile b/src/scripts/Makefile deleted file mode 100644 index 93f242d6..00000000 --- a/src/scripts/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -BASEDIR=../.. -include $(BASEDIR)/config.mk - -JSFILES = $(wildcard *.js) - -all: scripts.h - -clean: - $(RM) scripts.h - -scripts.h: $(JSFILES) - @echo "create $@ from *.js" - @for file in $(JSFILES); do \ - ./js2h.sh $$file >> $@; \ - done - -.PHONY: all clean - From fb2e39b0b79db2e690195a7f05ba97c43b680c11 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 00:41:10 +0200 Subject: [PATCH 072/201] fixes regeneration of scripts.h without this patch, the make target would keep appending updated js code to scripts.h and therefor create multiple instances of the C defines. --- src/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Makefile b/src/Makefile index 5ec16d82..d9c3d4d1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -22,6 +22,7 @@ config.h: @cp config.def.h $@ scripts/scripts.h: $(JSFILES) + $(RM) $@ @echo "create $@ from *.js" @for file in $(JSFILES); do \ ./scripts/js2h.sh $$file >> $@; \ From 846522be17881266a108ee8a897ca48ed2c1a8e1 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 00:43:49 +0200 Subject: [PATCH 073/201] changes make target dependencies technically, the vimb binary does not depend on webext, but the all target should build vimb and webext --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index d9c3d4d1..fab3cca2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,12 +5,12 @@ SUBDIRS = webextension OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) JSFILES = $(wildcard scripts/*.js) -all: vimb +all: vimb $(SUBDIRS:%=%.subdir-all) clean: $(SUBDIRS:%=%.subdir-clean) $(RM) vimb *.o scripts/scripts.h -vimb: $(OBJ) webextension.subdir-all +vimb: $(OBJ) $(CC) $(LDFLAGS) $(OBJ) -o $@ $(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h From cde47edcbcc0d6b13f06ecf5488424b59217f5a2 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 15:16:37 +0200 Subject: [PATCH 074/201] adds download eta display to status bar --- src/main.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 4da48c43..01664194 100644 --- a/src/main.c +++ b/src/main.c @@ -58,6 +58,8 @@ static gboolean on_webdownload_decide_destination(WebKitDownload *download, static void on_webdownload_failed(WebKitDownload *download, GError *error, Client *c); static void on_webdownload_finished(WebKitDownload *download, Client *c); +static void on_webdownload_received_data(WebKitDownload *download, + guint64 data_length, Client *c); static void on_webview_close(WebKitWebView *webview, Client *c); static WebKitWebView *on_webview_create(WebKitWebView *webview, WebKitNavigationAction *navact, Client *c); @@ -407,9 +409,47 @@ const char *vb_register_get(Client *c, char buf) return NULL; } +static void statusbar_update_downloads(Client *c, GString *status) +{ + GList *list; + guint list_length, remaining_max = 0; + gdouble progress, elapsed, total, remaining; + WebKitDownload *download; + + g_assert(c); + g_assert(status); + + if (c->state.downloads) { + list_length = g_list_length(c->state.downloads); + g_assert(list_length); + + /* get highest ETA value of all downloads based on each download's + * current progress fraction and time elapsed */ + for (list = c->state.downloads; list != NULL; list = list->next) { + download = (WebKitDownload *)list->data; + g_assert(download); + + progress = webkit_download_get_estimated_progress(download); + + /* avoid dividing by zero */ + if (progress == 0.0) { + continue; + } + + elapsed = webkit_download_get_elapsed_time(download); + total = (1.0 / progress) * elapsed; + remaining = total - elapsed; + + remaining_max = MAX(remaining, remaining_max); + } + + g_string_append_printf(status, " %d %s (ETA %us)", + list_length, list_length == 1? "dnld" : "dnlds", remaining_max); + } +} + void vb_statusbar_update(Client *c) { - int num; GString *status; if (!gtk_widget_get_visible(GTK_WIDGET(c->statusbar.box))) { @@ -417,11 +457,6 @@ void vb_statusbar_update(Client *c) } status = g_string_new(""); - /* show the active downloads */ - if (c->state.downloads) { - num = g_list_length(c->state.downloads); - g_string_append_printf(status, " %d %s", num, num == 1 ? "download" : "downloads"); - } /* show the number of matches search results */ if (c->state.search.matches) { @@ -449,6 +484,8 @@ void vb_statusbar_update(Client *c) #endif } + statusbar_update_downloads(c, status); + /* show the scroll status */ if (c->state.scroll_max == 0) { g_string_append(status, " All"); @@ -750,6 +787,7 @@ static void on_webctx_download_started(WebKitWebContext *webctx, g_signal_connect(download, "decide-destination", G_CALLBACK(on_webdownload_decide_destination), c); g_signal_connect(download, "failed", G_CALLBACK(on_webdownload_failed), c); g_signal_connect(download, "finished", G_CALLBACK(on_webdownload_finished), c); + g_signal_connect(download, "received-data", G_CALLBACK(on_webdownload_received_data), c); c->state.downloads = g_list_append(c->state.downloads, download); @@ -935,6 +973,25 @@ static void on_webdownload_finished(WebKitDownload *download, Client *c) } } +/** + * Callback for the webkit download received-data signal. + * This signal is emitted after response is received, every time new data has + * been written to the destination. It's useful to know the progress of the + * download operation. + */ +static void on_webdownload_received_data(WebKitDownload *download, + guint64 data_length, Client *c) +{ + /* rate limit statusbar updates */ + static gint64 statusbar_update_next = 0; + + if (g_get_monotonic_time() > statusbar_update_next) { + statusbar_update_next = g_get_monotonic_time() + 1000000; /* 1 second */ + + vb_statusbar_update(c); + } +} + /** * Callback for the webview close signal. */ From c4f7a6e71d2050dc7d3bace8980a7da6eda9779a Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 22:40:29 +0200 Subject: [PATCH 075/201] adds incsearch setting --- src/ex.c | 15 ++++++++++++++- src/main.h | 1 + src/setting.c | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ex.c b/src/ex.c index cc4bdbe9..46f02a78 100644 --- a/src/ex.c +++ b/src/ex.c @@ -348,6 +348,15 @@ VbResult ex_keypress(Client *c, int key) } } + if (c->config.incsearch && key != KEY_CR) { + gtk_text_buffer_get_bounds(buffer, &start, &end); + text = gtk_text_buffer_get_text(buffer, &start, &end, false); + if (text && (*text == '/' || *text == '?')) { + command_search(c, &((Arg){0, NULL})); /* stop last search */ + command_search(c, &((Arg){*text == '/' ? 1 : -1, (char*)text + 1})); + } + } + /* if the user deleted some content of the inputbox we check if the * inputbox is empty - if so we switch back to normal like vim does */ if (check_empty) { @@ -493,7 +502,11 @@ static void input_activate(Client *c) case '/': count = 1; /* fall through */ case '?': vb_enter(c, 'n'); - command_search(c, &((Arg){count, cmd})); + + /* start search, if incsearch, it's done while typing */ + if (!c->config.incsearch) { + command_search(c, &((Arg){count, cmd})); + } break; case ';': /* fall through */ diff --git a/src/main.h b/src/main.h index 59e1fbbc..9d016a5b 100644 --- a/src/main.h +++ b/src/main.h @@ -226,6 +226,7 @@ struct Client { GHashTable *settings; guint scrollstep; gboolean input_autohide; + gboolean incsearch; /* completion */ GdkRGBA comp_fg[COMP_LAST]; GdkRGBA comp_bg[COMP_LAST]; diff --git a/src/setting.c b/src/setting.c index 59bf50a9..9c5741f6 100644 --- a/src/setting.c +++ b/src/setting.c @@ -140,6 +140,7 @@ void setting_init(Client *c) i = 100; setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); setting_add(c, "download-path", TYPE_CHAR, &"~", NULL, 0, NULL); + setting_add(c, "incsearch", TYPE_BOOLEAN, &off, internal, 0, &c->config.incsearch); #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT /* gui style settings vimb2 compatibility */ From 768b5fdb4d2f3dd506fe286e90769e225cae9a2c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 9 Apr 2017 23:57:34 +0200 Subject: [PATCH 076/201] Fixed wrong default download path. --- src/setting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index 9c5741f6..6e1fcde4 100644 --- a/src/setting.c +++ b/src/setting.c @@ -139,7 +139,7 @@ void setting_init(Client *c) setting_add(c, "fullscreen", TYPE_BOOLEAN, &off, fullscreen, 0, NULL); i = 100; setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); - setting_add(c, "download-path", TYPE_CHAR, &"~", NULL, 0, NULL); + setting_add(c, "download-path", TYPE_CHAR, &"~/", NULL, 0, NULL); setting_add(c, "incsearch", TYPE_BOOLEAN, &off, internal, 0, &c->config.incsearch); #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT From 193fb5dcc8a0ba3775342c29eef64d1283acce88 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 10 Apr 2017 00:14:18 +0200 Subject: [PATCH 077/201] Open print dialog on :hardcopy. --- src/ex.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ex.c b/src/ex.c index 46f02a78..6818ba1d 100644 --- a/src/ex.c +++ b/src/ex.c @@ -829,7 +829,15 @@ static void ex_eval_javascript_finished(GObject *object, static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg) { - webkit_web_view_run_javascript(c->webview, "window.print();", NULL, NULL, NULL); + WebKitPrintOperation *op = webkit_print_operation_new(c->webview); + GtkPrintSettings *settings = gtk_print_settings_new(); + + gtk_print_settings_set(settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, c->state.title); + webkit_print_operation_set_print_settings(op, settings); + webkit_print_operation_run_dialog(op, NULL); + g_object_unref(op); + g_object_unref(settings); + return CMD_SUCCESS; } From 53fde940a0010479b0072552838db74c5855886b Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 10 Apr 2017 13:13:07 +0200 Subject: [PATCH 078/201] Install the extension in the right directory. --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 93cd476d..7bf97284 100644 --- a/config.mk +++ b/config.mk @@ -5,7 +5,7 @@ BINPREFIX := $(DESTDIR)$(PREFIX)/bin MANPREFIX := $(DESTDIR)$(PREFIX)/share/man EXAMPLEPREFIX := $(DESTDIR)$(PREFIX)/share/vimb/example DOTDESKTOPPREFIX := $(DESTDIR)$(PREFIX)/share/applications -RUNPREFIX := $(PREFIX) +RUNPREFIX := $(DESTDIR)$(PREFIX) EXTPREFIX := $(RUNPREFIX)/lib/vimb # define some directories From 2b6aeb694e8dca5da0ef52b2f44db12954cd0dcb Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 10 Apr 2017 13:35:40 +0200 Subject: [PATCH 079/201] Write url to history on load finished. --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index 01664194..65b188ed 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,7 @@ #include "ex.h" #include "ext-proxy.h" #include "handler.h" +#include "history.h" #include "input.h" #include "js.h" #include "main.h" @@ -1123,7 +1124,11 @@ static void on_webview_load_changed(WebKitWebView *webview, break; case WEBKIT_LOAD_FINISHED: + uri = webkit_web_view_get_uri(webview); c->state.progress = 100; + if (strncmp(uri, "about:", 6)) { + history_add(c, HISTORY_URL, uri, webkit_web_view_get_title(webview)); + } break; } } From c52088d89aa94d036da8ab5b8b7b47416c4996dd Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 10 Apr 2017 21:47:20 +0200 Subject: [PATCH 080/201] Fix wrong path to extension directory. --- Makefile | 6 +++--- config.mk | 7 ++++--- src/main.c | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 63a43939..8b8a9415 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ install: $(SRCDIR).subdir-all install -d $(BINPREFIX) install -m 755 $(SRCDIR)/vimb $(BINPREFIX)/vimb @# extension - install -d $(EXTPREFIX) - install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(EXTPREFIX)/$(EXTTARGET) + install -d $(LIBDIR) + install -m 644 $(SRCDIR)/webextension/$(EXTTARGET) $(LIBDIR)/$(EXTTARGET) @# man page install -d $(MANPREFIX)/man1 @sed -e "s!VERSION!$(VERSION)!g" \ @@ -27,7 +27,7 @@ install: $(SRCDIR).subdir-all install -m 644 vimb.desktop $(DOTDESKTOPPREFIX)/vimb.desktop uninstall: - $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(EXTPREFIX)/$(EXTTARGET) + $(RM) $(BINPREFIX)/vimb $(DESTDIR)$(MANDIR)/man1/vimb.1 $(LIBDIR)/$(EXTTARGET) clean: $(SRCDIR).subdir-clean diff --git a/config.mk b/config.mk index 7bf97284..2ca83c77 100644 --- a/config.mk +++ b/config.mk @@ -5,8 +5,9 @@ BINPREFIX := $(DESTDIR)$(PREFIX)/bin MANPREFIX := $(DESTDIR)$(PREFIX)/share/man EXAMPLEPREFIX := $(DESTDIR)$(PREFIX)/share/vimb/example DOTDESKTOPPREFIX := $(DESTDIR)$(PREFIX)/share/applications -RUNPREFIX := $(DESTDIR)$(PREFIX) -EXTPREFIX := $(RUNPREFIX)/lib/vimb +LIBDIR := $(DESTDIR)$(PREFIX)/lib/vimb +RUNPREFIX := $(PREFIX) +EXTENSIONDIR := $(RUNPREFIX)/lib/vimb # define some directories SRCDIR = src @@ -18,7 +19,7 @@ LIBS = gtk+-3.0 'webkit2gtk-4.0 >= 2.3.5' # setup general used CFLAGS CFLAGS += -std=c99 -pipe -Wall #CPPFLAGS += -pedantic -CPPFLAGS += -DVERSION=\"${VERSION}\" -DEXTPREFIX=\"${EXTPREFIX}\" +CPPFLAGS += -DVERSION=\"${VERSION}\" -DEXTENSIONDIR=\"${EXTENSIONDIR}\" CPPFLAGS += -DPROJECT=\"vimb\" -DPROJECT_UCFIRST=\"Vimb\" CPPFLAGS += -D_XOPEN_SOURCE=500 CPPFLAGS += -DGSEAL_ENABLE diff --git a/src/main.c b/src/main.c index 65b188ed..75023bbd 100644 --- a/src/main.c +++ b/src/main.c @@ -805,7 +805,7 @@ static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data GVariant *vdata; /* Setup the extension directory. */ - webkit_web_context_set_web_extensions_directory(webctx, EXTPREFIX); + webkit_web_context_set_web_extensions_directory(webctx, EXTENSIONDIR); name = ext_proxy_init(); vdata = g_variant_new("(ms)", name); From 382e219f05bbfb5e44c4e57a74eb299daddcda4d Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sat, 8 Apr 2017 23:44:36 +0200 Subject: [PATCH 081/201] adds make option V for verbose builds --- Makefile | 6 +++--- config.mk | 4 ++++ src/Makefile | 21 +++++++++++---------- src/webextension/Makefile | 8 ++++---- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 8b8a9415..74bc6b81 100644 --- a/Makefile +++ b/Makefile @@ -32,15 +32,15 @@ uninstall: clean: $(SRCDIR).subdir-clean sandbox: - @make $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install + $(Q)$(MAKE) $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install runsandbox: sandbox sandbox/usr/bin/vimb %.subdir-all: - @$(MAKE) $(MFLAGS) -C $* + $(Q)$(MAKE) $(MFLAGS) -C $* %.subdir-clean: - @$(MAKE) $(MFLAGS) -C $* clean + $(Q)$(MAKE) $(MFLAGS) -C $* clean .PHONY: all options install uninstall clean sandbox runsandbox diff --git a/config.mk b/config.mk index 2ca83c77..d09d8e69 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,9 @@ VERSION = dev-3.0 +ifneq ($(V),1) +Q := @ +endif + PREFIX ?= /usr/local BINPREFIX := $(DESTDIR)$(PREFIX)/bin MANPREFIX := $(DESTDIR)$(PREFIX)/share/man diff --git a/src/Makefile b/src/Makefile index fab3cca2..a3525973 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,31 +11,32 @@ clean: $(SUBDIRS:%=%.subdir-clean) $(RM) vimb *.o scripts/scripts.h vimb: $(OBJ) - $(CC) $(LDFLAGS) $(OBJ) -o $@ + $(Q)echo "${CC} $@" + $(Q)$(CC) $(LDFLAGS) $(OBJ) -o $@ $(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h -include $(OBJ:.o=.d) config.h: - @echo create $@ from config.def.h - @cp config.def.h $@ + $(Q)echo create $@ from config.def.h + $(Q)cp config.def.h $@ scripts/scripts.h: $(JSFILES) - $(RM) $@ - @echo "create $@ from *.js" - @for file in $(JSFILES); do \ + $(Q)$(RM) $@ + $(Q)echo "create $@ from *.js" + $(Q)for file in $(JSFILES); do \ ./scripts/js2h.sh $$file >> $@; \ done %.o: %.c - @echo "${CC} $@" - @$(CC) $(CFLAGS) -c -o $@ $< + $(Q)echo "${CC} $@" + $(Q)$(CC) $(CFLAGS) -c -o $@ $< %.subdir-all: config.h - @$(MAKE) $(MFLAGS) -C $* + $(Q)$(MAKE) $(MFLAGS) -C $* %.subdir-clean: - @$(MAKE) $(MFLAGS) -C $* clean + $(Q)$(MAKE) $(MFLAGS) -C $* clean .PHONY: all clean diff --git a/src/webextension/Makefile b/src/webextension/Makefile index c15c0885..58554e8b 100644 --- a/src/webextension/Makefile +++ b/src/webextension/Makefile @@ -9,11 +9,11 @@ clean: $(RM) $(EXTTARGET) *.lo $(EXTTARGET): $(OBJ) - @echo "$(CC) $@" - @$(CC) $(EXTLDFLAGS) ${OBJ} -o $@ + $(Q)echo "$(CC) $@" + $(Q)$(CC) $(EXTLDFLAGS) ${OBJ} -o $@ %.lo: %.c - @echo "${CC} $@" - @$(CC) $(EXTCFLAGS) -fPIC -c -o $@ $< + $(Q)echo "${CC} $@" + $(Q)$(CC) $(EXTCFLAGS) -fPIC -c -o $@ $< .PHONY: all clean From b83c174be0430868b477202c237bb572c4333045 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 9 Apr 2017 01:15:29 +0200 Subject: [PATCH 082/201] ensures correct initial input GtkTextView size --- src/main.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/main.c b/src/main.c index 75023bbd..ddadf8fb 100644 --- a/src/main.c +++ b/src/main.c @@ -1477,6 +1477,32 @@ void vb_gui_style_update(Client *c, const char *setting_name_new, const char *se /* Feed style sheet document to gtk */ gtk_css_provider_load_from_data(vb.style_provider, style_sheet->str, -1, NULL); + /* WORKAROUND to always ensure correct size of input field + * + * The following line is required to apply the style defined font size on + * the GtkTextView c->input. Without the call, the font size is updated on + * first user input, leading to a sudden unpleasant widget size and layout + * change. According to the GTK+ docs, this call should not be required as + * style context invalidation is automatic. + * + * "gtk_style_context_invalidate has been deprecated since version 3.12 + * and should not be used in newly-written code. Style contexts are + * invalidated automatically." + * https://developer.gnome.org/gtk3/stable/GtkStyleContext.html#gtk-style-context-invalidate + * + * Required settings in vimb config file: + * set input-autohide=true + * set input-font-normal=20pt monospace + * + * A bug has been filed at GTK+ + * https://bugzilla.gnome.org/show_bug.cgi?id=781158 + * + * Tested on ARCH linux with gtk3 3.22.10-1 + */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + gtk_style_context_invalidate(gtk_widget_get_style_context(c->input)); + G_GNUC_END_IGNORE_DEPRECATIONS; + cleanup: g_string_free(style_sheet, TRUE); } From 35abc0967ecd7c837a6b67639cf880bed059a7a4 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Apr 2017 09:13:20 +0200 Subject: [PATCH 083/201] Don't show mode label in status bar if not needed. --- src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.c b/src/main.c index ddadf8fb..f1a12e8e 100644 --- a/src/main.c +++ b/src/main.c @@ -351,7 +351,6 @@ void vb_modelabel_update(Client *c, const char *label) } else { vb_echo(c, MSG_NORMAL, FALSE, "%s", label); } - gtk_label_set_text(GTK_LABEL(c->statusbar.mode), label); } /** From 190480775f6cfa0de0794a4c0f6ad274aade0294 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Apr 2017 09:35:18 +0200 Subject: [PATCH 084/201] Put objects before linker flags. This allows to compile with -Wl,--as-needed option which affects only those libraries written after that option on the command line. --- src/Makefile | 2 +- src/webextension/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index a3525973..9794ac0d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,7 +12,7 @@ clean: $(SUBDIRS:%=%.subdir-clean) vimb: $(OBJ) $(Q)echo "${CC} $@" - $(Q)$(CC) $(LDFLAGS) $(OBJ) -o $@ + $(Q)$(CC) $(OBJ) $(LDFLAGS) -o $@ $(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h diff --git a/src/webextension/Makefile b/src/webextension/Makefile index 58554e8b..77b7c826 100644 --- a/src/webextension/Makefile +++ b/src/webextension/Makefile @@ -10,7 +10,7 @@ clean: $(EXTTARGET): $(OBJ) $(Q)echo "$(CC) $@" - $(Q)$(CC) $(EXTLDFLAGS) ${OBJ} -o $@ + $(Q)$(CC) $(OBJ) $(EXTLDFLAGS) -o $@ %.lo: %.c $(Q)echo "${CC} $@" From 9a66a879e7f33911315b4cad4dd13d5477b0e8b6 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Apr 2017 09:42:38 +0200 Subject: [PATCH 085/201] Don't verbose the echo lines during make. The echo lines where a light alternative to the long compiler options lines, so there is no real benefit on showing the 'echo' itself. --- src/Makefile | 8 ++++---- src/webextension/Makefile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index 9794ac0d..ae8d14bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -11,7 +11,7 @@ clean: $(SUBDIRS:%=%.subdir-clean) $(RM) vimb *.o scripts/scripts.h vimb: $(OBJ) - $(Q)echo "${CC} $@" + @echo "${CC} $@" $(Q)$(CC) $(OBJ) $(LDFLAGS) -o $@ $(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h @@ -19,18 +19,18 @@ $(OBJ): config.h $(BASEDIR)/config.mk scripts/scripts.h -include $(OBJ:.o=.d) config.h: - $(Q)echo create $@ from config.def.h + @echo create $@ from config.def.h $(Q)cp config.def.h $@ scripts/scripts.h: $(JSFILES) $(Q)$(RM) $@ - $(Q)echo "create $@ from *.js" + @echo "create $@ from *.js" $(Q)for file in $(JSFILES); do \ ./scripts/js2h.sh $$file >> $@; \ done %.o: %.c - $(Q)echo "${CC} $@" + @echo "${CC} $@" $(Q)$(CC) $(CFLAGS) -c -o $@ $< %.subdir-all: config.h diff --git a/src/webextension/Makefile b/src/webextension/Makefile index 77b7c826..215629cb 100644 --- a/src/webextension/Makefile +++ b/src/webextension/Makefile @@ -9,11 +9,11 @@ clean: $(RM) $(EXTTARGET) *.lo $(EXTTARGET): $(OBJ) - $(Q)echo "$(CC) $@" + @echo "$(CC) $@" $(Q)$(CC) $(OBJ) $(EXTLDFLAGS) -o $@ %.lo: %.c - $(Q)echo "${CC} $@" + @echo "${CC} $@" $(Q)$(CC) $(EXTCFLAGS) -fPIC -c -o $@ $< .PHONY: all clean From e5383c9d3d9888570bde38a433f95af8751e86df Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Tue, 11 Apr 2017 11:12:17 +0200 Subject: [PATCH 086/201] removes todos from readme --- README.md | 76 ------------------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/README.md b/README.md index 7e47a947..ab7496d0 100644 --- a/README.md +++ b/README.md @@ -76,82 +76,6 @@ To run vimb without installation you could run as a sandbox like this 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` option for the make - - [x] establish communication channel between the vimb instance and the now - required webextension (dbus) - - [x] use propper authorization for dbus peers - - [ ] queue settings that needs to be pushed to the webextension until the - dbus connection is established. -2. migrate as many of the features of the webkit1 vimb - - [x] 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 - - [x] 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 - - [x] download support - - [ ] editor command - - [ ] external downloader - - [ ] hinting - - [x] searching and matching of search results - - [x] navigation j, k, h, l, ... - - [x] history and history lookup - - [ ] completion - - [ ] augroup - - [ ] autocmd - - [x] bookmarks - - [x] file paths for :source and :save - - [x] search phrases - - [x] settings - - [x] url handler - - [x] url history - - [ ] HSTS - - [ ] auto-response-header - - [x] cookies support - - [x] register support and `:register` command - - [x] read it later queue - - [ ] show scroll indicator in statusbar as top, x%, bottom or all - how can we get this information from webview easily? - - [x] find a way to disable the scrollbars on the main frame - Can be achieved by `document.documentElement.style.overflow = - 'hidden';` in _scripts.js_ - - [ ] 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 - - [x] default zoom - - [x] yanking - - [x] keymapping - - [x] 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) - - [x] 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. From be67e57c4f790ed43ca340dab0c453ff1ad396ca Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Apr 2017 12:05:11 +0200 Subject: [PATCH 087/201] Fixed webinspector not opend on second attempt. If the webinspector was opened and closed via 'gF' it could not be opened again with this key combination. --- src/normal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/normal.c b/src/normal.c index e71a4f16..be5e9878 100644 --- a/src/normal.c +++ b/src/normal.c @@ -767,7 +767,7 @@ static VbResult normal_view_inspector(Client *c, const NormalCmdInfo *info) /* 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)) { + if (webkit_web_inspector_is_attached(inspector)) { webkit_web_inspector_close(inspector); } else if (webkit_settings_get_enable_developer_extras(settings)) { webkit_web_inspector_show(inspector); From cec450c50e5f40f6ea3b72f3b65423f527f1ca80 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 11 Apr 2017 21:46:44 +0200 Subject: [PATCH 088/201] Merge into changelog entries of 2.12 release. --- CHANGELOG.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68621092..43479982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,33 @@ ### Removed -* `FEATURE_COOKIE` precompiler flag was removed bcause compiling without cookie +* `FEATURE_COOKIE` precompiler flag was removed because compiling without cookie support does not bring any real benefit +## [2.12] - 2017-04-11 + +### Added + +* Queueing of key events - fixes swalled chars in case of some imap bindings + #258 (thanks to Michael Mackus) +* Allow to disable xembed by `FEATURE_NO_XEMBED` to compile on wayland only + platforms (thanks to Patrick Steinhardt) +* Custom default_zoom setting disables HIGH_DPI logic (thanks to Robert Timm) +* Allow link activation from search result via `` #131 + +### Changed + +* Allow shortcuts without parameters #329 +* Write soup cache to disk after each page load to allow other instances to + pick this up. +* Use the beginning position of links for hinting (thanks to Yutao Yuan) + +### Fixed + +* Fix path expansion to accept only valid POSIX.1-2008 usernames (thanks to + Manzur Mukhitdinov) +* Fix default previouspattern (thanks to Nicolas Porcel) + ## [2.11] - 2015-12-17 ### Added @@ -52,4 +76,5 @@ * Fixed none POSIX `echo -n` call [unreleased]: https://github.com/fanglingsu/vimb/compare/2.11...HEAD +[2.12]: https://github.com/fanglingsu/vimb/compare/2.11...2.12 [2.11]: https://github.com/fanglingsu/vimb/compare/2.10...2.11 From 246fd134d3850610d4c6d49f02783ae82e1b63fa Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 12 Apr 2017 00:03:03 +0200 Subject: [PATCH 089/201] Use script message handler to track elements focus changes. Use script message handler instead of dbus to track for focused editable elements to switch vimb into input mode or back to normal mode. --- src/ext-proxy.c | 49 +-------------------------- src/main.c | 27 +++++++++++++++ src/scripts/vimb_util.js | 38 +++++++++++++++++++++ src/util.c | 30 +++++++++++++++++ src/util.h | 2 ++ src/webextension/ext-main.c | 66 ++++--------------------------------- 6 files changed, 104 insertions(+), 108 deletions(-) create mode 100644 src/scripts/vimb_util.js diff --git a/src/ext-proxy.c b/src/ext-proxy.c index b17f0814..daf04ced 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -32,10 +32,6 @@ static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, gpointer data); 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_web_extension_page_created(GDBusConnection *connection, const char *sender_name, const char *object_path, const char *interface_name, const char *signal_name, @@ -188,42 +184,6 @@ static void dbus_call(Client *c, const char *method, GVariant *param, 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 */ -} - -/** - * 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. * @@ -248,13 +208,6 @@ static void on_web_extension_page_created(GDBusConnection *connection, /* Set the dbus proxy on the right client based on page id. */ p->dbusproxy = (GDBusProxy*)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); + /* TODO Subscribe to debus signals here. */ } } diff --git a/src/main.c b/src/main.c index f1a12e8e..8ae8f529 100644 --- a/src/main.c +++ b/src/main.c @@ -91,6 +91,8 @@ static void vimb_cleanup(void); #endif static void vimb_setup(void); static WebKitWebView *webview_new(Client *c, WebKitWebView *webview); +static void on_script_message_focus(WebKitUserContentManager *manager, + WebKitJavascriptResult *message, Client *c); struct Vimb vb; @@ -1554,9 +1556,34 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) webkit_user_script_unref(script); } + /* Inject the global utility script. */ + script = webkit_user_script_new(VIMB_UTIL, + 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); + + /* Setup script message handlers. */ + webkit_user_content_manager_register_script_message_handler(ucm, "focus"); + g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); + return new; } +static void on_script_message_focus(WebKitUserContentManager *manager, + WebKitJavascriptResult *message, Client *c) +{ + gboolean is_focused; + + is_focused = (gboolean)util_js_result_as_number(message); + /* 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'); + } +} + int main(int argc, char* argv[]) { Client *c; diff --git a/src/scripts/vimb_util.js b/src/scripts/vimb_util.js new file mode 100644 index 00000000..df747fa6 --- /dev/null +++ b/src/scripts/vimb_util.js @@ -0,0 +1,38 @@ +/* Utility object injected into all frames. */ +var vimb = Object.freeze((function(){ + 'use strict'; + /* Checks if given element is editable */ + function isEditable(e) { + if (e) { + var name = e.tagName.toLowerCase(); + return e.isContentEditable + || name == "textarea" + || (name == "input" && /^(?:color|date|datetime|datetime-local|email|month|number|password|search|tel|text|time|url|week)$/i.test(e.type)); + } + return false; + } + + return { + init: function() { + var b = document.body; + b.addEventListener( + "focus", + function(ev) { + var e = ev.target, n; + if (isEditable(e)) { + window.webkit.messageHandlers.focus.postMessage(1); + } + }, + true + ); + b.addEventListener( + "blur", + function() { + window.webkit.messageHandlers.focus.postMessage(0); + }, + true + ); + } + }; +})()); +vimb.init(); diff --git a/src/util.c b/src/util.c index 29557f2b..3430a585 100644 --- a/src/util.c +++ b/src/util.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,35 @@ gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const cha return found; } +char *util_js_result_as_string(WebKitJavascriptResult *result) +{ + JSValueRef value; + JSStringRef string; + size_t len; + char *retval = NULL; + + value = webkit_javascript_result_get_value(result); + string = JSValueToStringCopy(webkit_javascript_result_get_global_context(result), + value, NULL); + + len = JSStringGetMaximumUTF8CStringSize(string); + if (len) { + retval = g_malloc(len); + JSStringGetUTF8CString(string, retval, len); + } + JSStringRelease(string); + + return retval; +} + +double util_js_result_as_number(WebKitJavascriptResult *result) +{ + JSValueRef value = webkit_javascript_result_get_value(result); + + return JSValueToNumber(webkit_javascript_result_get_global_context(result), value, + NULL); +} + /** * Reads given input and try to parse ~/, ~user, $VAR or ${VAR} expansion * from the start of the input and moves the input pointer to the first diff --git a/src/util.h b/src/util.h index ca9ba68c..a9fd042c 100644 --- a/src/util.h +++ b/src/util.h @@ -43,6 +43,8 @@ GList *util_file_to_unique_list(const char *filename, Util_Content_Func func, guint max_items); gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src); gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const char *input); +char *util_js_result_as_string(WebKitJavascriptResult *result); +double util_js_result_as_number(WebKitJavascriptResult *result); 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); diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index ae1fe72c..97e42617 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -36,13 +36,13 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, static void emit_page_created(GDBusConnection *connection, guint64 pageid); static void emit_page_created_pending(GDBusConnection *connection); static void queue_page_created_signal(guint64 pageid); +#if 0 static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, GVariant *data); +#endif 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 void on_editable_change_focus(WebKitDOMEventTarget *target, - WebKitDOMEvent *event, WebKitWebExtension *extension); static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data); static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension); static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, @@ -57,9 +57,6 @@ static const GDBusInterfaceVTable interface_vtable = { static const char introspection_xml[] = "" " " - " " - " " - " " " " " " " " @@ -180,6 +177,7 @@ static void on_dbus_connection_created(GObject *source_object, static void add_onload_event_observers(WebKitDOMDocument *doc, WebKitWebExtension *extension) { +#if 0 /* might soon be use for some events */ WebKitDOMEventTarget *target; /* Add the document to the table of known documents or if already exists @@ -192,15 +190,7 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, * 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, extension); - webkit_dom_event_target_add_event_listener(target, "blur", - G_CALLBACK(on_editable_change_focus), TRUE, extension); - /* 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, extension); +#endif } /** @@ -255,6 +245,7 @@ static void queue_page_created_signal(guint64 pageid) ext.page_created_signals = g_array_append_val(ext.page_created_signals, pageid); } +#if 0 /** * Emits a signal over dbus. * @@ -279,6 +270,7 @@ static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, g_error_free(error); } } +#endif /** * Handle dbus method calls. @@ -304,52 +296,6 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, } } -/** - * 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, WebKitWebExtension *extension) -{ - 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, extension); - 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", extension, - g_variant_new("(b)", input_focus)); - } -} - /** * Callback for web extensions page-created signal. */ From ca8bb26b96a78824a241903211dd5dc3d6cdccee Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 12 Apr 2017 00:32:29 +0200 Subject: [PATCH 090/201] User scripts don't work when scripts are disabled. In case the scripts are injected from the ui, they are not processed in case JavaScript is disabled in vimb. So use the previous dbus logic to observe dom focus changes and use the script messaging system to inform the ui process. --- src/main.c | 7 ---- src/scripts/vimb_util.js | 38 -------------------- src/webextension/ext-main.c | 70 +++++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 47 deletions(-) delete mode 100644 src/scripts/vimb_util.js diff --git a/src/main.c b/src/main.c index 8ae8f529..0486d6fd 100644 --- a/src/main.c +++ b/src/main.c @@ -1556,13 +1556,6 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) webkit_user_script_unref(script); } - /* Inject the global utility script. */ - script = webkit_user_script_new(VIMB_UTIL, - 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); - /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); diff --git a/src/scripts/vimb_util.js b/src/scripts/vimb_util.js deleted file mode 100644 index df747fa6..00000000 --- a/src/scripts/vimb_util.js +++ /dev/null @@ -1,38 +0,0 @@ -/* Utility object injected into all frames. */ -var vimb = Object.freeze((function(){ - 'use strict'; - /* Checks if given element is editable */ - function isEditable(e) { - if (e) { - var name = e.tagName.toLowerCase(); - return e.isContentEditable - || name == "textarea" - || (name == "input" && /^(?:color|date|datetime|datetime-local|email|month|number|password|search|tel|text|time|url|week)$/i.test(e.type)); - } - return false; - } - - return { - init: function() { - var b = document.body; - b.addEventListener( - "focus", - function(ev) { - var e = ev.target, n; - if (isEditable(e)) { - window.webkit.messageHandlers.focus.postMessage(1); - } - }, - true - ); - b.addEventListener( - "blur", - function() { - window.webkit.messageHandlers.focus.postMessage(0); - }, - true - ); - } - }; -})()); -vimb.init(); diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index 97e42617..3abf4bfb 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -43,6 +43,8 @@ static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, 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 void on_editable_change_focus(WebKitDOMEventTarget *target, + WebKitDOMEvent *event, WebKitWebExtension *extension); static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data); static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension); static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, @@ -177,7 +179,6 @@ static void on_dbus_connection_created(GObject *source_object, static void add_onload_event_observers(WebKitDOMDocument *doc, WebKitWebExtension *extension) { -#if 0 /* might soon be use for some events */ WebKitDOMEventTarget *target; /* Add the document to the table of known documents or if already exists @@ -190,7 +191,15 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, * 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)); -#endif + + webkit_dom_event_target_add_event_listener(target, "focus", + G_CALLBACK(on_editable_change_focus), TRUE, extension); + webkit_dom_event_target_add_event_listener(target, "blur", + G_CALLBACK(on_editable_change_focus), TRUE, extension); + /* 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, extension); } /** @@ -296,6 +305,63 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, } } +/** + * 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, WebKitWebExtension *extension) +{ + gboolean input_focus; + WebKitDOMDocument *doc; + WebKitDOMDOMWindow *dom_window; + 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); + } + + dom_window = webkit_dom_document_get_default_view(doc); + if (!dom_window) { + return; + } + + 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, extension); + 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; + + webkit_dom_document_get_default_view(doc); + if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", input_focus ? "1" : "0")) { + g_warning("Error sending focus message"); + return; + } + } + g_object_unref(dom_window); +} + /** * Callback for web extensions page-created signal. */ From fd062109dac91cb114d8d8c8bd80388d5eaa2053 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 14 Apr 2017 22:37:36 +0200 Subject: [PATCH 091/201] adds reading html from stdin (closes #346) as already implemented on the master branch --- src/main.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main.c b/src/main.c index f1a12e8e..2bb51258 100644 --- a/src/main.c +++ b/src/main.c @@ -80,6 +80,7 @@ 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 read_from_stdin(Client *c); static void register_cleanup(Client *c); static void update_title(Client *c); static void update_urlbar(Client *c); @@ -1239,6 +1240,31 @@ static gboolean quit(Client *c) return FALSE; } +/** + * Read string from stdin and pass it to webkit for html interpretation. + */ +static void read_from_stdin(Client *c) +{ + GIOChannel *ch; + gchar *buf = NULL; + GError *err = NULL; + gsize len = 0; + + g_assert(c); + + ch = g_io_channel_unix_new(fileno(stdin)); + 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_html(c->webview, buf, NULL); + } + g_free(buf); +} + /** * Free the register contents memory. */ @@ -1605,6 +1631,9 @@ int main(int argc, char* argv[]) c = client_new(NULL, TRUE); if (argc <= 1) { vb_load_uri(c, &(Arg){TARGET_CURRENT, NULL}); + } else if (!strcmp(argv[argc - 1], "-")) { + /* read from stdin if uri is - */ + read_from_stdin(c); } else { vb_load_uri(c, &(Arg){TARGET_CURRENT, argv[argc - 1]}); } From 5c7059196850118f4e9435797cedd155661748af Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 20 Apr 2017 13:44:41 +0200 Subject: [PATCH 092/201] Add docu for reading from stdin. --- doc/vimb.1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 92f64798..58ee702c 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -15,13 +15,16 @@ great editor. .SH SYNOPSIS .B vimb .OP OPTIONS -.RI [ URI "|" file ] +.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 From 597a0acfd9e35f6516a6c5069fdf651416e9f11c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 20 Apr 2017 22:45:31 +0200 Subject: [PATCH 093/201] Added :save command. --- src/command.c | 9 +-- src/main.c | 150 ++++++++++++++++++++++++++++---------------------- src/main.h | 3 +- src/util.c | 49 +++++++++++++++++ src/util.h | 2 + 5 files changed, 141 insertions(+), 72 deletions(-) diff --git a/src/command.c b/src/command.c index 7fbc8785..4913ddc7 100644 --- a/src/command.c +++ b/src/command.c @@ -143,8 +143,8 @@ gboolean command_yank(Client *c, const Arg *arg, char buf) gboolean command_save(Client *c, const Arg *arg) { -#if 0 const char *uri, *path = NULL; + WebKitDownload *download; if (arg->i == COMMAND_SAVE_CURRENT) { uri = c->state.uri; @@ -159,10 +159,11 @@ gboolean command_save(Client *c, const Arg *arg) if (!uri || !*uri) { return FALSE; } -#endif - /* TODO start the download to given path here */ - return TRUE; + /* Start the download to given path. */ + download = webkit_web_view_download_uri(c->webview, uri); + + return vb_download_set_destination(c, download, NULL, path); } #ifdef FEATURE_QUEUE diff --git a/src/main.c b/src/main.c index 67e34863..7b0f4bac 100644 --- a/src/main.c +++ b/src/main.c @@ -97,6 +97,86 @@ static void on_script_message_focus(WebKitUserContentManager *manager, struct Vimb vb; +/** + * Set the destination for a download according to suggested file name and + * possible given path. + */ +gboolean vb_download_set_destination(Client *c, WebKitDownload *download, + char *suggested_filename, const char *path) +{ + char *download_path, *dir, *file, *uri; + download_path = GET_CHAR(c, "download-path"); + + /* For unnamed downloads set default filename. */ + if (!suggested_filename || !*suggested_filename) { + suggested_filename = "vimb-download"; + } + + /* Prepare the path to save the download. */ + if (path && *path) { + file = util_build_path(c, path, download_path); + + /* 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, suggested_filename, NULL); + g_free(dir); + } + } else { + file = util_build_path(c, suggested_filename, download_path); + } + + /* If the filepath exists already insert numerical suffix before file + * extension. */ + if (g_file_test(file, G_FILE_TEST_EXISTS)) { + const char *dot_pos; + char *num = NULL; + GString *tmp; + gssize suffix; + int i = 1; + suffix = 2; + + /* position on .tar. (special case, extension with two dots), + * position on last dot (if any) otherwise */ + if (!(dot_pos = strstr(file, ".tar."))) { + dot_pos = strrchr(file, '.'); + } + + /* the position to insert the suffix at */ + if (dot_pos) { + suffix = dot_pos - file; + } else { + suffix = strlen(file); + } + + tmp = g_string_new(NULL); + num = g_strdup_printf("%d", i++); + + /* Construct a new complete odwnload filepath with suffic before the + * file extension. */ + do { + num = g_strdup_printf("%d", i++); + g_string_assign(tmp, file); + g_string_insert(tmp, suffix, num); + g_free(num); + } while (g_file_test(tmp->str, G_FILE_TEST_EXISTS)); + + file = g_strdup(tmp->str); + g_string_free(tmp, TRUE); + } + + /* Build URI from filepath. */ + uri = g_filename_to_uri(file, NULL, NULL); + g_free(file); + + /* configure download */ + g_assert(uri); + webkit_download_set_allow_overwrite(download, FALSE); + webkit_download_set_destination(download, uri); + g_free(uri); + + return TRUE; +} /** * Write text to the inpubox if this isn't focused. @@ -822,75 +902,11 @@ static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data static gboolean on_webdownload_decide_destination(WebKitDownload *download, gchar *suggested_filename, Client *c) { - g_assert(download); - g_assert(suggested_filename); - g_assert(c); - - char *path, *filename, *uri; - GString *expanded, *filepath; - const char *extension_dot; - int suffix; - gssize suffix_pos; - - /* get the download path from settings */ - path = GET_CHAR(c, "download-path"); - g_assert(path); - - /* expand any ~ or $VAR patterns in download path */ - expanded = g_string_new(NULL); - util_parse_expansion(c, (const char**)&path, expanded, UTIL_EXP_TILDE|UTIL_EXP_DOLLAR, ""); - g_string_append(expanded, path + 1); - - /* for unnamed downloads set default filename */ - filename = strlen(suggested_filename) ? suggested_filename : "vimb-download"; - - /* construct complete download filepath */ - filepath = g_string_new(NULL); - g_string_printf(filepath, "%s%c%s", expanded->str, G_DIR_SEPARATOR, filename); - - /* if the filepath exists already - * insert numerical suffix before file extension */ - if (g_file_test(filepath->str, G_FILE_TEST_EXISTS)) { - suffix = 2; - - /* position on .tar. (special case, extension with two dots), - * position on last dot (if any) otherwise */ - if (!(extension_dot = strstr(filename, ".tar."))) { - extension_dot = strrchr(filename, '.'); - } - - /* the position to insert the suffix at */ - if (extension_dot) { - suffix_pos = extension_dot - filename; - } else { - suffix_pos = strlen(filename); - } - - /* construct a new complete download filepath and add the suffix before - * the filename extension, keep incrementing the suffix value as long - * as the filepath exists, stop on first unused filename. */ - do { - g_string_printf(filepath, "%s%c%.*s_%i%s", - expanded->str, G_DIR_SEPARATOR, - (int)suffix_pos, filename, - suffix++, filename + suffix_pos); - } while (g_file_test(filepath->str, G_FILE_TEST_EXISTS)); + if (webkit_download_get_destination(download)) { + return TRUE; } - /* build URI from filepath */ - uri = g_filename_to_uri(filepath->str, NULL, NULL); - g_assert(uri); - - /* configure download */ - webkit_download_set_allow_overwrite(download, FALSE); - webkit_download_set_destination(download, uri); - - /* cleanup */ - g_string_free(expanded, TRUE); - g_string_free(filepath, TRUE); - g_free(uri); - - return TRUE; + return vb_download_set_destination(c, download, suggested_filename, NULL); } /** diff --git a/src/main.h b/src/main.h index 9d016a5b..e4b969da 100644 --- a/src/main.h +++ b/src/main.h @@ -263,7 +263,8 @@ struct Vimb { GtkCssProvider *style_provider; }; - +gboolean vb_download_set_destination(Client *c, WebKitDownload *download, + char *suggested_filename, const char *path); 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); diff --git a/src/util.c b/src/util.c index 3430a585..b392876a 100644 --- a/src/util.c +++ b/src/util.c @@ -36,6 +36,48 @@ static struct { static void create_dir_if_not_exists(const char *dirpath); +/** + * Build the absolute file path of given path and possible given directory. + * + * Returned path must be freed. + */ +char *util_build_path(Client *c, 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(c, 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(c, dir, expflags))) { + /* use expanded dir and append expanded path */ + fullPath = g_build_filename(dexp, fexp, NULL); + g_free(dexp); + } + g_free(fexp); + } + } + + /* if full path not found use current dir */ + if (!fullPath) { + fullPath = g_build_filename(g_get_current_dir(), path, NULL); + } + + /* Create the directory part of the path if it does not exists. */ + if ((p = strrchr(fullPath, '/'))) { + *p = '\0'; + util_create_dir_if_not_exists(fullPath); + *p = '/'; + } + + return fullPath; +} /** * Free memory for allocated path strings. @@ -47,6 +89,13 @@ void util_cleanup(void) } } +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); + } +} + /** * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated * string. diff --git a/src/util.h b/src/util.h index a9fd042c..63f50461 100644 --- a/src/util.h +++ b/src/util.h @@ -30,7 +30,9 @@ enum { }; typedef void *(*Util_Content_Func)(const char*, const char*); +char *util_build_path(Client *c, const char *path, const char *dir); void util_cleanup(void); +void util_create_dir_if_not_exists(const char *dirpath); 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, ...); From 1b39ab0ba4e4ecad746c934bd89ba0938ff0e04d Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 20 Apr 2017 22:58:59 +0200 Subject: [PATCH 094/201] Show if directory can't be created. --- src/main.c | 4 ++++ src/util.c | 20 ++++++++++++++++---- src/util.h | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index 7b0f4bac..85b3c531 100644 --- a/src/main.c +++ b/src/main.c @@ -126,6 +126,10 @@ gboolean vb_download_set_destination(Client *c, WebKitDownload *download, file = util_build_path(c, suggested_filename, download_path); } + if (!file) { + return FALSE; + } + /* If the filepath exists already insert numerical suffix before file * extension. */ if (g_file_test(file, G_FILE_TEST_EXISTS)) { diff --git a/src/util.c b/src/util.c index b392876a..94d8fb1c 100644 --- a/src/util.c +++ b/src/util.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -71,9 +72,16 @@ char *util_build_path(Client *c, const char *path, const char *dir) /* Create the directory part of the path if it does not exists. */ if ((p = strrchr(fullPath, '/'))) { + gboolean res; *p = '\0'; - util_create_dir_if_not_exists(fullPath); + res = util_create_dir_if_not_exists(fullPath); *p = '/'; + + if (!res) { + g_free(fullPath); + + return NULL; + } } return fullPath; @@ -89,11 +97,15 @@ void util_cleanup(void) } } -void util_create_dir_if_not_exists(const char *dirpath) +gboolean 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); + if (!g_mkdir_with_parents(dirpath, 0755)) { + g_critical("Could not create directory '%s': %s", dirpath, strerror(errno)); + + return FALSE; } + + return TRUE; } /** diff --git a/src/util.h b/src/util.h index 63f50461..c1830a22 100644 --- a/src/util.h +++ b/src/util.h @@ -32,7 +32,7 @@ typedef void *(*Util_Content_Func)(const char*, const char*); char *util_build_path(Client *c, const char *path, const char *dir); void util_cleanup(void); -void util_create_dir_if_not_exists(const char *dirpath); +gboolean util_create_dir_if_not_exists(const char *dirpath); 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, ...); From 5f65084c7082b71f4509e1b17841f8fbff80d918 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 00:03:55 +0200 Subject: [PATCH 095/201] Run js for scrolling from webextension #367. --- src/ext-proxy.c | 5 +++++ src/ext-proxy.h | 1 + src/normal.c | 14 +++++++------- src/webextension/ext-main.c | 15 ++++++++++++++- src/webextension/ext-util.c | 21 +++++++++++++++++++++ src/webextension/ext-util.h | 2 ++ 6 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index daf04ced..ea958fe5 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -153,6 +153,11 @@ static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, NULL); } +void ext_proxy_eval_script(Client *c, char *js) +{ + dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL); +} + /** * Request the web extension to focus first editable element. * Returns whether an focusable element was found or not. diff --git a/src/ext-proxy.h b/src/ext-proxy.h index 8258df9d..5d4a0738 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -23,6 +23,7 @@ #include "main.h" const char *ext_proxy_init(void); +void ext_proxy_eval_script(Client *c, char *js); void ext_proxy_focus_input(Client *c); void ext_proxy_set_header(Client *c, const char *headers); diff --git a/src/normal.c b/src/normal.c index be5e9878..b7d9234d 100644 --- a/src/normal.c +++ b/src/normal.c @@ -688,19 +688,19 @@ static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) 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); + ext_proxy_eval_script(c, js); 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); + ext_proxy_eval_script(c, "window.scroll(window.scrollX, document.body.scrollHeight);"); return RESULT_COMPLETE; case '0': - webkit_web_view_run_javascript(c->webview, "window.scroll(0, window.scrollY);", NULL, NULL, NULL); + ext_proxy_eval_script(c, "window.scroll(0, window.scrollY);"); return RESULT_COMPLETE; case '$': - webkit_web_view_run_javascript(c->webview, "window.scroll(document.body.scrollWidth, window.scrollY);", NULL, NULL, NULL); + ext_proxy_eval_script(c, "window.scroll(document.body.scrollWidth, window.scrollY);"); return RESULT_COMPLETE; default: if (info->key2 == 'g') { @@ -708,18 +708,18 @@ static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) 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); + ext_proxy_eval_script(c, js); 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); + ext_proxy_eval_script(c, "window.scroll(window.scrollX, 0);"); return RESULT_COMPLETE; } return RESULT_ERROR; } js = g_strdup_printf("window.scrollBy(%d,%d);", x, y); - webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL); + ext_proxy_eval_script(c, js); g_free(js); return RESULT_COMPLETE; diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index 3abf4bfb..661ba289 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -59,6 +59,9 @@ static const GDBusInterfaceVTable interface_vtable = { static const char introspection_xml[] = "" " " + " " + " " + " " " " " " " " @@ -290,7 +293,17 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, { char *value; - if (!g_strcmp0(method, "FocusInput")) { + if (!g_strcmp0(method, "EvalJsNoResult")) { + g_variant_get(parameters, "(s)", &value); + JSGlobalContextRef jsContext; + + jsContext = webkit_frame_get_javascript_context_for_script_world( + webkit_web_page_get_main_frame(ext.webpage), + webkit_script_world_get_default() + ); + JSValueRef ref = NULL; + ext_util_js_eval(jsContext, value, &ref); + } else 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")) { diff --git a/src/webextension/ext-util.c b/src/webextension/ext-util.c index f63cb11b..facb4a7e 100644 --- a/src/webextension/ext-util.c +++ b/src/webextension/ext-util.c @@ -23,6 +23,27 @@ #include "../config.h" #include "ext-util.h" +/** + * Evaluates given string as script and return if this call succeed or not. + */ +gboolean ext_util_js_eval(JSContextRef ctx, const char *script, JSValueRef *result) +{ + JSStringRef js_str; + JSValueRef exc = NULL, res = NULL; + + js_str = JSStringCreateWithUTF8CString(script); + res = JSEvaluateScript(ctx, js_str, JSContextGetGlobalObject(ctx), NULL, 0, &exc); + JSStringRelease(js_str); + + if (exc) { + *result = exc; + return FALSE; + } + + *result = res; + return TRUE; +} + /** * Creates a temporary file with given content. * diff --git a/src/webextension/ext-util.h b/src/webextension/ext-util.h index a4286a5a..877933a1 100644 --- a/src/webextension/ext-util.h +++ b/src/webextension/ext-util.h @@ -21,7 +21,9 @@ #define _EXT_UTIL_H #include +#include gboolean ext_util_create_tmp_file(const char *content, char **file); +gboolean ext_util_js_eval(JSContextRef ctx, const char *script, JSValueRef *result); #endif /* end of include guard: _EXT_UTIL_H */ From 0035771d5a174e6d96bf1b1c66fde4813fcd6135 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 01:07:48 +0200 Subject: [PATCH 096/201] Run js by webextension. Make sure all the scripts we run internally are also evaluated in case js is disabled. --- src/ex.c | 53 +++++++++++++------------------------ src/ext-proxy.c | 8 ++++-- src/ext-proxy.h | 2 +- src/input.c | 4 +-- src/normal.c | 23 ++++++++-------- src/webextension/ext-main.c | 26 +++++++++++++++--- src/webextension/ext-util.c | 22 +++++++++++++++ src/webextension/ext-util.h | 1 + 8 files changed, 83 insertions(+), 56 deletions(-) diff --git a/src/ex.c b/src/ex.c index 6818ba1d..79191b2f 100644 --- a/src/ex.c +++ b/src/ex.c @@ -39,6 +39,7 @@ #include "setting.h" #include "shortcut.h" #include "util.h" +#include "ext-proxy.h" typedef enum { /* TODO add feature autocmd */ @@ -126,8 +127,7 @@ 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 void ex_eval_javascript_finished(GObject *object, - GAsyncResult *result, Client *c); +static void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, Client *c); 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); @@ -784,47 +784,30 @@ static VbCmdResult ex_eval(Client *c, const ExArg *arg) { /* Called as :eval! - don't print to inputbox. */ if (arg->bang) { - webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, NULL, NULL); + ext_proxy_eval_script(c, arg->rhs->str, NULL); } else { - webkit_web_view_run_javascript(c->webview, arg->rhs->str, NULL, - (GAsyncReadyCallback)ex_eval_javascript_finished, c); + ext_proxy_eval_script(c, arg->rhs->str, (GAsyncReadyCallback)on_eval_script_finished); } return CMD_SUCCESS; } -static void ex_eval_javascript_finished(GObject *object, - GAsyncResult *result, Client *c) +static void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, Client *c) { - WebKitJavascriptResult *js_result; - JSValueRef value; - JSGlobalContextRef context; - GError *error = NULL; - - js_result = webkit_web_view_run_javascript_finish(WEBKIT_WEB_VIEW(object), result, &error); - if (!js_result) { - vb_echo(c, MSG_ERROR, TRUE, "%s", error->message); - g_error_free(error); - - return; - } - - context = webkit_javascript_result_get_global_context(js_result); - value = webkit_javascript_result_get_value(js_result); - if (JSValueIsString(context, value)) { - JSStringRef str_ref; - char *string; - size_t len; - - str_ref = JSValueToStringCopy(context, value, NULL); - len = JSStringGetMaximumUTF8CStringSize(str_ref); - string = g_new(char, len); - JSStringGetUTF8CString(str_ref, string, len); - JSStringRelease(str_ref); - vb_echo(c, MSG_NORMAL, FALSE, "%s", string); - g_free(string); + gboolean success = FALSE; + char *string = NULL; + + GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); + if (return_value) { + g_variant_get(return_value, "(bs)", &success, &string); + if (success) { + vb_echo(c, MSG_NORMAL, FALSE, "%s", string); + } else { + vb_echo(c, MSG_ERROR, TRUE, "%s", string); + } + } else { + vb_echo(c, MSG_ERROR, TRUE, ""); } - webkit_javascript_result_unref(js_result); } static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index ea958fe5..6957f97f 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -153,9 +153,13 @@ static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, NULL); } -void ext_proxy_eval_script(Client *c, char *js) +void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) { - dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL); + if (callback) { + dbus_call(c, "EvalJs", g_variant_new("(s)", js), callback); + } else { + dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL); + } } /** diff --git a/src/ext-proxy.h b/src/ext-proxy.h index 5d4a0738..7c70c723 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -23,7 +23,7 @@ #include "main.h" const char *ext_proxy_init(void); -void ext_proxy_eval_script(Client *c, char *js); +void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback); void ext_proxy_focus_input(Client *c); void ext_proxy_set_header(Client *c, const char *headers); diff --git a/src/input.c b/src/input.c index 6236c9d7..3e7943c1 100644 --- a/src/input.c +++ b/src/input.c @@ -38,7 +38,7 @@ void input_enter(Client *c) * disturbing the user */ gtk_widget_grab_focus(GTK_WIDGET(c->webview)); vb_modelabel_update(c, "-- INPUT --"); - webkit_web_view_run_javascript(c->webview, "var vimb_input_mode_element = document.activeElement;", NULL, NULL, NULL); + ext_proxy_eval_script(c, "var vimb_input_mode_element = document.activeElement;", NULL); } /** @@ -46,7 +46,7 @@ void input_enter(Client *c) */ void input_leave(Client *c) { - webkit_web_view_run_javascript(c->webview, "vimb_input_mode_element.blur();", NULL, NULL, NULL); + ext_proxy_eval_script(c, "vimb_input_mode_element.blur();", NULL); vb_modelabel_update(c, ""); } diff --git a/src/normal.c b/src/normal.c index b7d9234d..31cdaa4e 100644 --- a/src/normal.c +++ b/src/normal.c @@ -28,6 +28,7 @@ #include "normal.h" #include "scripts/scripts.h" #include "util.h" +#include "ext-proxy.h" typedef enum { PHASE_START, @@ -321,7 +322,7 @@ void pass_enter(Client *c) */ void pass_leave(Client *c) { - webkit_web_view_run_javascript(c->webview, "document.activeElement.blur();", NULL, NULL, NULL); + ext_proxy_eval_script(c, "document.activeElement.blur();", NULL); vb_modelabel_update(c, ""); } @@ -422,9 +423,7 @@ static VbResult normal_fire(Client *c, const NormalCmdInfo *info) * highlight. We use the search_matches as indicator that the searching is * active. */ if (c->state.search.active) { - webkit_web_view_run_javascript(c->webview, - "getSelection().anchorNode.parentNode.click();", NULL, NULL, - NULL); + ext_proxy_eval_script(c, "getSelection().anchorNode.parentNode.click();", NULL); return RESULT_COMPLETE; } @@ -497,7 +496,7 @@ static VbResult normal_increment_decrement(Client *c, const NormalCmdInfo *info) int count = info->count ? info->count : 1; js = g_strdup_printf(INCREMENT_URI_NUMBER, info->key == CTRL('A') ? count : -count); - webkit_web_view_run_javascript(c->webview, js, NULL, NULL, NULL); + ext_proxy_eval_script(c, js, NULL); g_free(js); return RESULT_COMPLETE; @@ -688,19 +687,19 @@ static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) js = g_strdup_printf( "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));", info->count); - ext_proxy_eval_script(c, js); + ext_proxy_eval_script(c, js, NULL); g_free(js); return RESULT_COMPLETE; } /* Without count scroll to the end of the page. */ - ext_proxy_eval_script(c, "window.scroll(window.scrollX, document.body.scrollHeight);"); + ext_proxy_eval_script(c, "window.scroll(window.scrollX, document.body.scrollHeight);", NULL); return RESULT_COMPLETE; case '0': - ext_proxy_eval_script(c, "window.scroll(0, window.scrollY);"); + ext_proxy_eval_script(c, "window.scroll(0, window.scrollY);", NULL); return RESULT_COMPLETE; case '$': - ext_proxy_eval_script(c, "window.scroll(document.body.scrollWidth, window.scrollY);"); + ext_proxy_eval_script(c, "window.scroll(document.body.scrollWidth, window.scrollY);", NULL); return RESULT_COMPLETE; default: if (info->key2 == 'g') { @@ -708,18 +707,18 @@ static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) js = g_strdup_printf( "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));", info->count); - ext_proxy_eval_script(c, js); + ext_proxy_eval_script(c, js, NULL); g_free(js); return RESULT_COMPLETE; } /* Without count gg scrolls to the top of the page. */ - ext_proxy_eval_script(c, "window.scroll(window.scrollX, 0);"); + ext_proxy_eval_script(c, "window.scroll(window.scrollX, 0);", NULL); return RESULT_COMPLETE; } return RESULT_ERROR; } js = g_strdup_printf("window.scrollBy(%d,%d);", x, y); - ext_proxy_eval_script(c, js); + ext_proxy_eval_script(c, js, NULL); g_free(js); return RESULT_COMPLETE; diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index 661ba289..46a8233a 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -59,6 +59,11 @@ static const GDBusInterfaceVTable interface_vtable = { static const char introspection_xml[] = "" " " + " " + " " + " " + " " + " " " " " " " " @@ -293,16 +298,29 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, { char *value; - if (!g_strcmp0(method, "EvalJsNoResult")) { - g_variant_get(parameters, "(s)", &value); + if (g_str_has_prefix(method, "EvalJs")) { + char *result = NULL; + gboolean success; + gboolean no_result = !g_strcmp0(method, "EvalJsNoResult"); + JSValueRef ref = NULL; JSGlobalContextRef jsContext; + g_variant_get(parameters, "(s)", &value); + jsContext = webkit_frame_get_javascript_context_for_script_world( webkit_web_page_get_main_frame(ext.webpage), webkit_script_world_get_default() ); - JSValueRef ref = NULL; - ext_util_js_eval(jsContext, value, &ref); + + success = ext_util_js_eval(jsContext, value, &ref); + + if (no_result) { + g_dbus_method_invocation_return_value(invocation, NULL); + } else { + result = ext_util_js_ref_to_string(jsContext, ref); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(bs)", success, result)); + g_free(result); + } } else 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); diff --git a/src/webextension/ext-util.c b/src/webextension/ext-util.c index facb4a7e..bdf55d42 100644 --- a/src/webextension/ext-util.c +++ b/src/webextension/ext-util.c @@ -79,3 +79,25 @@ gboolean ext_util_create_tmp_file(const char *content, char **file) return TRUE; } + +/** + * Returns a new allocates string for given value reference. + * String must be freed if not used anymore. + */ +char* ext_util_js_ref_to_string(JSContextRef ctx, JSValueRef ref) +{ + char *string; + size_t len; + JSStringRef str_ref; + + g_return_val_if_fail(ref != NULL, NULL); + + str_ref = JSValueToStringCopy(ctx, ref, NULL); + len = JSStringGetMaximumUTF8CStringSize(str_ref); + + string = g_new0(char, len); + JSStringGetUTF8CString(str_ref, string, len); + JSStringRelease(str_ref); + + return string; +} diff --git a/src/webextension/ext-util.h b/src/webextension/ext-util.h index 877933a1..2629dbcc 100644 --- a/src/webextension/ext-util.h +++ b/src/webextension/ext-util.h @@ -25,5 +25,6 @@ gboolean ext_util_create_tmp_file(const char *content, char **file); gboolean ext_util_js_eval(JSContextRef ctx, const char *script, JSValueRef *result); +char* ext_util_js_ref_to_string(JSContextRef ctx, JSValueRef ref); #endif /* end of include guard: _EXT_UTIL_H */ From 9ef97faea7ad2855c589f5c1241a6fc2b784bf92 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 17 Apr 2017 22:40:39 +0200 Subject: [PATCH 097/201] adds call to mode input_changed (closes #369) --- src/main.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main.c b/src/main.c index 85b3c531..a71f351d 100644 --- a/src/main.c +++ b/src/main.c @@ -51,6 +51,7 @@ 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_textbuffer_changed(GtkTextBuffer *textbuffer, gpointer user_data); static void on_webctx_download_started(WebKitWebContext *webctx, WebKitDownload *download, Client *c); static void on_webctx_init_web_extension(WebKitWebContext *webctx, gpointer data); @@ -687,6 +688,7 @@ static Client *client_new(WebKitWebView *webview, gboolean show) 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)); + g_signal_connect(c->buffer, "changed", G_CALLBACK(on_textbuffer_changed), c); /* Make sure the user can see the typed text. */ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(c->input), GTK_WRAP_WORD_CHAR); @@ -802,6 +804,37 @@ static void mode_free(Mode *mode) g_slice_free(Mode, mode); } +/** + * The ::changed signal is emitted when the content of a GtkTextBuffer has + * changed. This call back function is connected to the input box' text buffer. + */ +static void on_textbuffer_changed(GtkTextBuffer *textbuffer, gpointer user_data) +{ + gchar *text; + GtkTextIter start, end; + Client *c = (Client *)user_data; + + g_assert(c); + +#if 0 + /* don't observe changes in completion mode */ + if (c->mode->flags & FLAG_COMPLETION) { + return; + } +#endif + + /* don't process changes not typed by the user */ + if (gtk_widget_is_focus(c->input) && c->mode && c->mode->input_changed) { + + gtk_text_buffer_get_bounds(textbuffer, &start, &end); + text = gtk_text_buffer_get_text(textbuffer, &start, &end, false); + + c->mode->input_changed(c, text); + + g_free(text); + } +} + /** * Set the style of the statusbar. */ From e6f4bc018e6378e3d3c4572fa8c4bedf4f69f708 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 17 Apr 2017 22:41:15 +0200 Subject: [PATCH 098/201] moves incsearch to ex_input_changed (closes #370) --- src/ex.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ex.c b/src/ex.c index 79191b2f..20e0eaab 100644 --- a/src/ex.c +++ b/src/ex.c @@ -348,15 +348,6 @@ VbResult ex_keypress(Client *c, int key) } } - if (c->config.incsearch && key != KEY_CR) { - gtk_text_buffer_get_bounds(buffer, &start, &end); - text = gtk_text_buffer_get_text(buffer, &start, &end, false); - if (text && (*text == '/' || *text == '?')) { - command_search(c, &((Arg){0, NULL})); /* stop last search */ - command_search(c, &((Arg){*text == '/' ? 1 : -1, (char*)text + 1})); - } - } - /* if the user deleted some content of the inputbox we check if the * inputbox is empty - if so we switch back to normal like vim does */ if (check_empty) { @@ -398,6 +389,13 @@ void ex_input_changed(Client *c, const char *text) case 'g': /* TODO create hints */ break; + case '/': /* fall through */ + case '?': + if (c->config.incsearch) { + command_search(c, &((Arg){0, NULL})); /* stop last search */ + command_search(c, &((Arg){*text == '/' ? 1 : -1, (char*)text + 1})); + } + break; } } From 80290d463ba671facc17fd2b55daa5d45b61d8f7 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 17 Apr 2017 22:41:47 +0200 Subject: [PATCH 099/201] adds TODO for deprecated gdkkeysyms --- src/map.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/map.c b/src/map.c index d5b1768b..7cf92042 100644 --- a/src/map.c +++ b/src/map.c @@ -55,6 +55,9 @@ static struct { char one; char two; } special_keys[] = { + /* TODO: In GTK3, keysyms changed to have a KEY_ prefix. + * See gdkkeysyms.h and gdkkeysyms-compat.h + */ {GDK_SHIFT_MASK, GDK_Tab, 'k', 'B'}, {0, GDK_Up, 'k', 'u'}, {0, GDK_Down, 'k', 'd'}, From 3343d86284998a34190ac02de116dcb7bb97aa4e Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Mon, 17 Apr 2017 23:22:39 +0200 Subject: [PATCH 100/201] adds TODO for text buffer change warning --- src/ex.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ex.c b/src/ex.c index 20e0eaab..0949f553 100644 --- a/src/ex.c +++ b/src/ex.c @@ -380,6 +380,16 @@ void ex_input_changed(Client *c, const char *text) gtk_text_buffer_get_iter_at_line(buffer, &start, 0); if (gtk_text_iter_forward_to_line_end(&start)) { gtk_text_buffer_get_end_iter(buffer, &end); + + /* TODO the following line creates a GTK warning. + * ex_input_changed() is called from the "changed" event handler of + * GtkTextBuffer. Apparently it's not supported to change a text + * buffer in the changed handler!? + * + * Gtk-WARNING **: Invalid text buffer iterator: either the + * iterator is uninitialized, or the characters/pixbufs/widgets in + * the buffer have been modified since the iterator was created. + */ gtk_text_buffer_delete(buffer, &start, &end); } } From 17556517c68e070d6680de4a5cbf9b4d410aadda Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 11:05:05 +0200 Subject: [PATCH 101/201] Clear input after load commited during search. If a new page is opened during search, the search string was still present in the input box even if the searching is paused in this case. So now clear the input box and stop searching in case of a new page load. --- src/command.c | 1 + src/main.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/command.c b/src/command.c index 4913ddc7..fdea7082 100644 --- a/src/command.c +++ b/src/command.c @@ -45,6 +45,7 @@ gboolean command_search(Client *c, const Arg *arg) c->state.search.matches = 0; c->state.search.active = FALSE; vb_statusbar_update(c); + vb_echo(c, MSG_NORMAL, FALSE, ""); return TRUE; } diff --git a/src/main.c b/src/main.c index 85b3c531..76b87792 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #include #include "ascii.h" +#include "command.h" #include "completion.h" #include "config.h" #include "ex.h" @@ -1143,6 +1144,9 @@ static void on_webview_load_changed(WebKitWebView *webview, /* clear possible set marks */ marks_clear(c); + /* Unset possible last search. */ + command_search(c, &((Arg){0})); + break; case WEBKIT_LOAD_FINISHED: From 7198464f2b83b67c0848b6a0db4283b07da96c95 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 11:45:32 +0200 Subject: [PATCH 102/201] Fixed none printed :open .. on O. If the input was filled programmatically and the mode where switched the already filled input was cleaned by the attempt to stop possible search. So now the input is only cleared if there is really an active search. --- src/command.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index fdea7082..1ffb109d 100644 --- a/src/command.c +++ b/src/command.c @@ -41,11 +41,15 @@ gboolean command_search(Client *c, const Arg *arg) fc = webkit_web_view_get_find_controller(c->webview); if (arg->i == 0) { + /* Clear the input only if the search is active. */ + if (c->state.search.active) { + vb_echo(c, MSG_NORMAL, FALSE, ""); + } webkit_find_controller_search_finish(fc); c->state.search.matches = 0; c->state.search.active = FALSE; vb_statusbar_update(c); - vb_echo(c, MSG_NORMAL, FALSE, ""); + return TRUE; } From d69df8f1b9392f887d307f05963ffd5247dadca7 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:07:54 +0200 Subject: [PATCH 103/201] Removed hsts file because this is not used at the time. --- src/main.c | 1 - src/main.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main.c b/src/main.c index 089f5171..05a3239d 100644 --- a/src/main.c +++ b/src/main.c @@ -1436,7 +1436,6 @@ static void vimb_setup(void) 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); diff --git a/src/main.h b/src/main.h index e4b969da..a2ab13a2 100644 --- a/src/main.h +++ b/src/main.h @@ -111,7 +111,6 @@ enum { FILES_CONFIG, FILES_COOKIE, FILES_HISTORY, - FILES_HSTS, FILES_QUEUE, FILES_SCRIPT, FILES_SEARCH, From 798aa1b38fd48cd8a664b7ea166a028d9ca4dc49 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:10:39 +0200 Subject: [PATCH 104/201] Don not double inject user script file. The script file is injected in case 'userscripts' setting is enabled. So there is no need to do this on creating the webview too. --- src/main.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main.c b/src/main.c index 05a3239d..7a97f511 100644 --- a/src/main.c +++ b/src/main.c @@ -1600,8 +1600,6 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) { WebKitWebView *new; WebKitUserContentManager *ucm; - WebKitUserScript *script; - char *js = NULL; /* create a new webview */ if (webview) { @@ -1629,15 +1627,6 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) g_signal_connect(webkit_web_context_get_default(), "download-started", G_CALLBACK(on_webctx_download_started), c); - /* 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); - } - /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); From fe0ace52d36043e93cef91720f41f1ef38f4dd0d Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:16:46 +0200 Subject: [PATCH 105/201] Don't warn about missing scripts file. It's not required that the user has a scripts file. So don't attempt to inject it if it is not present. --- src/setting.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index 6e1fcde4..f0752e8b 100644 --- a/src/setting.c +++ b/src/setting.c @@ -593,7 +593,9 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, ucm = webkit_web_view_get_user_content_manager(c->webview); if (enabled) { - if (g_file_get_contents(vb.files[FILES_SCRIPT], &source, NULL, NULL)) { + if (vb.files[FILES_SCRIPT] + && 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 From f9875fde81440fced02830f5341a689212f4ffe9 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:29:50 +0200 Subject: [PATCH 106/201] Don't fail on missing files. --- src/util.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/util.c b/src/util.c index 94d8fb1c..be3b5c4d 100644 --- a/src/util.c +++ b/src/util.c @@ -151,7 +151,7 @@ gboolean util_file_append(const char *file, const char *format, ...) va_list args; FILE *f; - if ((f = fopen(file, "a+"))) { + if (file && (f = fopen(file, "a+"))) { flock(fileno(f), LOCK_EX); va_start(args, format); @@ -178,6 +178,9 @@ gboolean util_file_prepend(const char *file, const char *format, ...) va_list args; char *content; FILE *f; + if (!file) { + return FALSE; + } content = util_get_file_contents(file, NULL); if ((f = fopen(file, "w"))) { @@ -213,12 +216,17 @@ gboolean util_file_prepend(const char *file, const char *format, ...) */ char *util_file_pop_line(const char *file, int *item_count) { - char **lines = util_get_lines(file); + char **lines; char *new, *line = NULL; int len, count = 0; + if (!file) { + return NULL; + } + lines = util_get_lines(file); + if (lines) { len = g_strv_length(lines); if (len) { @@ -261,7 +269,7 @@ char *util_get_file_contents(const char *filename, gsize *length) GError *error = NULL; char *content = NULL; - if (!g_file_get_contents(filename, &content, length, &error)) { + if (filename && !g_file_get_contents(filename, &content, length, &error)) { g_warning("Cannot open %s: %s", filename, error->message); g_error_free(error); } @@ -308,9 +316,14 @@ char *util_get_filepath(const char *dir, const char *filename, gboolean create) */ char **util_get_lines(const char *filename) { - char *content = util_get_file_contents(filename, NULL); + char *content; char **lines = NULL; - if (content) { + + if (!filename) { + return NULL; + } + + if ((content = util_get_file_contents(filename, NULL))) { /* split the file content into lines */ lines = g_strsplit(content, "\n", -1); g_free(content); From 24281bb68f889d526aa84443d4420927ffce6059 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:51:14 +0200 Subject: [PATCH 107/201] Create files needed for full feature support. Create all those files on startup that are needed to provide all the documented features. --- src/main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 7a97f511..77cb4ecd 100644 --- a/src/main.c +++ b/src/main.c @@ -1430,15 +1430,15 @@ static void vimb_setup(void) } /* 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_CLOSED] = util_get_filepath(path, "closed", TRUE); + vb.files[FILES_COOKIE] = util_get_filepath(path, "cookies", TRUE); 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_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); + vb.files[FILES_HISTORY] = util_get_filepath(path, "history", TRUE); + vb.files[FILES_COMMAND] = util_get_filepath(path, "command", TRUE); + vb.files[FILES_BOOKMARK] = util_get_filepath(path, "bookmark", TRUE); + vb.files[FILES_QUEUE] = util_get_filepath(path, "queue", TRUE); + vb.files[FILES_SEARCH] = util_get_filepath(path, "search", TRUE); g_free(path); /* Use seperate rendering processed for the webview of the clients in the From 048fd12d199f0891a4dd8f72bc5fec0332e8033a Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 21 Apr 2017 23:55:14 +0200 Subject: [PATCH 108/201] Update the license year. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 77cb4ecd..0c8b6da2 100644 --- a/src/main.c +++ b/src/main.c @@ -1672,7 +1672,7 @@ int main(int argc, char* argv[]) if (ver) { fprintf(stdout, "%s, version %s\n\n", PROJECT, VERSION); - fprintf(stdout, "Copyright © 2012 - 2016 Daniel Carl \n"); + fprintf(stdout, "Copyright © 2012 - 2017 Daniel Carl \n"); fprintf(stdout, "License GPLv3+: GNU GPL version 3 or later \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"); From f2a1f136a27cf8f1c177766ee4a45b710b8a70d1 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 22 Apr 2017 22:45:01 +0200 Subject: [PATCH 109/201] Remove FEATURE_HISTORY_INDICATOR. This does not bring any real benefit to make allow to avoid this but makes the code hard to maintain. --- src/main.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main.c b/src/main.c index 0c8b6da2..0b353043 100644 --- a/src/main.c +++ b/src/main.c @@ -1363,13 +1363,7 @@ static void update_title(Client *c) */ static void update_urlbar(Client *c) { -#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); - -#ifdef FEATURE_HISTORY_INDICATOR gboolean back, fwd; back = webkit_web_view_can_go_back(c->webview); @@ -1379,11 +1373,9 @@ static void update_urlbar(Client *c) if (back || fwd) { g_string_append_printf(str, " [%s]", back ? (fwd ? "-+" : "-") : "+"); } -#endif /* FEATURE_HISTORY_INDICATOR */ gtk_label_set_text(GTK_LABEL(c->statusbar.left), str->str); g_string_free(str, TRUE); -#endif /* !defined(FEATURE_HISTORY_INDICATOR) */ } #ifdef FREE_ON_QUIT From 099ce3237e27c05624240fde6436ca90bd77980e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 22 Apr 2017 22:46:11 +0200 Subject: [PATCH 110/201] Add profile option #343. --- doc/vimb.1 | 5 +++++ src/config.def.h | 2 -- src/main.c | 33 +++++++++++++++++++++++++++++++-- src/main.h | 1 + src/util.c | 18 +++++++++++++++--- src/util.h | 3 ++- 6 files changed, 54 insertions(+), 8 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 58ee702c..50297907 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -39,6 +39,11 @@ of an XEmbed-aware application, that Vimb will use as its parent. .B "\-h, \-\-help" Show help options. .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 .B "\-v, \-\-version" Print build and version information. .SH MODES diff --git a/src/config.def.h b/src/config.def.h index 999a6602..2682a9d9 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -18,8 +18,6 @@ */ /* features */ -/* should the history indicator [+-] be shown in status bar after url */ -#define FEATURE_HISTORY_INDICATOR /* show wget style progressbar in status bar */ #define FEATURE_WGET_PROGRESS_BAR /* show load progress in window title */ diff --git a/src/main.c b/src/main.c index 0b353043..6b014ea5 100644 --- a/src/main.c +++ b/src/main.c @@ -96,6 +96,8 @@ static void vimb_setup(void); static WebKitWebView *webview_new(Client *c, WebKitWebView *webview); static void on_script_message_focus(WebKitUserContentManager *manager, WebKitJavascriptResult *message, Client *c); +static gboolean profileOptionArgFunc(const gchar *option_name, + const gchar *value, gpointer data, GError **error); struct Vimb vb; @@ -882,21 +884,38 @@ static void set_title(Client *c, const char *title) static void spawn_new_instance(const char *uri, gboolean embed) { guint i = 0; - char xid[64]; - char *cmd[5]; + /* memory allocation */ + char **cmd = g_malloc_n( + 3 /* basename + uri + ending NULL */ +#ifndef FEATURE_NO_XEMBED + + (vb.embed && embed ? 2 : 0) +#endif + + (vb.profile ? 2 : 0), + sizeof(char *) + ); cmd[i++] = vb.argv0; +#ifndef FEATURE_NO_XEMBED if (vb.embed && embed) { + char xid[64]; cmd[i++] = "-e"; snprintf(xid, LENGTH(xid), "%d", (int)vb.embed); cmd[i++] = xid; } +#endif + if (vb.profile) { + cmd[i++] = "-p"; + cmd[i++] = vb.profile; + } cmd[i++] = (char*)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); } /** @@ -1398,6 +1417,7 @@ static void vimb_cleanup(void) g_free(vb.files[i]); } } + g_free(vb.profile); } #endif @@ -1640,6 +1660,14 @@ static void on_script_message_focus(WebKitUserContentManager *manager, } } +static gboolean profileOptionArgFunc(const gchar *option_name, + const gchar *value, gpointer data, GError **error) +{ + vb.profile = util_sanitize_filename(g_strdup(value)); + + return TRUE; +} + int main(int argc, char* argv[]) { Client *c; @@ -1650,6 +1678,7 @@ int main(int argc, char* argv[]) 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}, + {"profile", 'p', 0, G_OPTION_ARG_CALLBACK, profileOptionArgFunc, "Profile name", NULL}, {"version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Print version", NULL}, {NULL} }; diff --git a/src/main.h b/src/main.h index a2ab13a2..36fac576 100644 --- a/src/main.h +++ b/src/main.h @@ -256,6 +256,7 @@ struct Vimb { GHashTable *modes; /* all available browser main modes */ char *configfile; /* config file given as option on startup */ char *files[FILES_LAST]; + char *profile; /* profile name */ struct { guint history_max; } config; diff --git a/src/util.c b/src/util.c index be3b5c4d..8f1c2b45 100644 --- a/src/util.c +++ b/src/util.c @@ -35,6 +35,8 @@ static struct { char *config_dir; } util; +extern struct Vimb vb; + static void create_dir_if_not_exists(const char *dirpath); /** @@ -248,12 +250,12 @@ char *util_file_pop_line(const char *file, int *item_count) } /** - * Retrieves the config directory path. - * Returnes string must be freed. + * Retrieves the config directory path according to current used profile. + * Returned string must be freed. */ char *util_get_config_dir(void) { - char *path = g_build_filename(g_get_user_config_dir(), PROJECT, NULL); + char *path = g_build_filename(g_get_user_config_dir(), PROJECT, vb.profile, NULL); create_dir_if_not_exists(path); return path; @@ -645,6 +647,16 @@ gboolean util_parse_expansion(Client *c, const char **input, GString *str, return expanded; } +/** + * Sanituze filename by removeing directory separator by underscore. + * + * The string is modified in place. + */ +char *util_sanitize_filename(char *filename) +{ + return g_strdelimit(filename, G_DIR_SEPARATOR_S, '_'); +} + char *util_strcasestr(const char *haystack, const char *needle) { guchar c1, c2; diff --git a/src/util.h b/src/util.h index c1830a22..f416e5c0 100644 --- a/src/util.h +++ b/src/util.h @@ -49,7 +49,8 @@ char *util_js_result_as_string(WebKitJavascriptResult *result); double util_js_result_as_number(WebKitJavascriptResult *result); 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); +char *util_sanitize_filename(char *filename); char *util_strcasestr(const char *haystack, const char *needle); +char *util_str_replace(const char* search, const char* replace, const char* string); #endif /* end of include guard: _UTIL_H */ From 5a14c82381bd11c4b7be4f9e6d0030aaba84c7d4 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 22 Apr 2017 22:53:49 +0200 Subject: [PATCH 111/201] Show profile name in url bar #343. --- src/main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 6b014ea5..32c50d78 100644 --- a/src/main.c +++ b/src/main.c @@ -1382,13 +1382,21 @@ static void update_title(Client *c) */ static void update_urlbar(Client *c) { - GString *str = g_string_new(c->state.uri); + GString *str; gboolean back, fwd; - back = webkit_web_view_can_go_back(c->webview); - fwd = webkit_web_view_can_go_forward(c->webview); + str = g_string_new(""); + /* show profile name */ + if (vb.profile) { + g_string_append_printf(str, "[%s] ", vb.profile); + } + + /* show current url */ + g_string_append_printf(str, "%s", c->state.uri); /* show history indicator only if there is something to show */ + back = webkit_web_view_can_go_back(c->webview); + fwd = webkit_web_view_can_go_forward(c->webview); if (back || fwd) { g_string_append_printf(str, " [%s]", back ? (fwd ? "-+" : "-") : "+"); } From b837489d8a0895c7d2a8ed5e13bbf0ebf733e3bf Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 22 Apr 2017 23:04:37 +0200 Subject: [PATCH 112/201] Added missed --config option on spawning new instance. --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index 32c50d78..8e38de20 100644 --- a/src/main.c +++ b/src/main.c @@ -887,6 +887,7 @@ static void spawn_new_instance(const char *uri, gboolean embed) /* memory allocation */ char **cmd = g_malloc_n( 3 /* basename + uri + ending NULL */ + + (vb.configfile ? 2 : 0) #ifndef FEATURE_NO_XEMBED + (vb.embed && embed ? 2 : 0) #endif @@ -896,6 +897,10 @@ static void spawn_new_instance(const char *uri, gboolean embed) cmd[i++] = vb.argv0; + if (vb.configfile) { + cmd[i++] = "-c"; + cmd[i++] = vb.configfile; + } #ifndef FEATURE_NO_XEMBED if (vb.embed && embed) { char xid[64]; From eb741c953d4b06bef6529f42641b3520ed992cd6 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 23 Apr 2017 00:55:48 +0200 Subject: [PATCH 113/201] fixes incsearch history handling Refactores parts of command_search() (closes #372) Fixes search using "/ register (type "/") --- src/command.c | 90 ++++++++++++++++++++++++++++++--------------------- src/command.h | 2 +- src/ex.c | 8 ++--- src/main.c | 2 +- src/main.h | 4 +-- src/normal.c | 11 +++---- 6 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/command.c b/src/command.c index 1ffb109d..204bf05d 100644 --- a/src/command.c +++ b/src/command.c @@ -31,67 +31,85 @@ #include "history.h" #include "main.h" -extern struct Vimb vb; - -gboolean command_search(Client *c, const Arg *arg) +gboolean command_search(Client *c, const Arg *arg, bool commit) { - const char *query; - gboolean forward; WebKitFindController *fc; + const char *query; + guint count; fc = webkit_web_view_get_find_controller(c->webview); + + g_assert(c); + g_assert(arg); + g_assert(fc); + if (arg->i == 0) { + webkit_find_controller_search_finish(fc); + /* Clear the input only if the search is active. */ if (c->state.search.active) { vb_echo(c, MSG_NORMAL, FALSE, ""); } - webkit_find_controller_search_finish(fc); + + c->state.search.active = FALSE; + c->state.search.direction = 0; 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 */ - c->state.search.direction = arg->i > 0 ? 1 : -1; - query = arg->s; - /* add new search query to history and search register */ - 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(c, '/'); - } + query = arg->s; + count = abs(arg->i); - forward = (arg->i * c->state.search.direction) > 0; + /* Only committed search strings adjust registers and are recorded in + * history, intermediate strings (while using incsearch) don't. */ + if (commit) { + if (query) { + history_add(c, HISTORY_SEARCH, query, NULL); + vb_register_add(c, '/', query); + } else { + /* Committed search without string re-searches last string. */ + query = vb_register_get(c, '/'); + } + } + /* Hand the query string to webkit's find controller. */ if (query) { - 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); - /* TODO get the number of matches */ - c->state.search.active = TRUE; - /* Skip first search because the first match is already - * highlighted on search start. */ - count -= 1; + /* Force a fresh start in order to have webkit select the first match + * on the page. Without this workaround the first selected match + * depends on the most recent selection or caret position (even when + * caret browsing is disabled). */ + if(commit) { + webkit_find_controller_search(fc, "", WEBKIT_FIND_OPTIONS_NONE, G_MAXUINT); } - if (forward) { + webkit_find_controller_search(fc, query, + WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | + WEBKIT_FIND_OPTIONS_WRAP_AROUND | + (arg->i > 0 ? WEBKIT_FIND_OPTIONS_NONE : WEBKIT_FIND_OPTIONS_BACKWARDS), + G_MAXUINT); + + c->state.search.active = TRUE; + c->state.search.direction = arg->i > 0 ? 1 : -1; + /* TODO get the number of matches */ + + /* Skip first search because the first match is already + * highlighted on search start. */ + count -= 1; + } + + /* Step through searchs result focus according to arg->i. */ + if (c->state.search.active) { + if (arg->i * c->state.search.direction > 0) { while (count--) { webkit_find_controller_search_next(fc); - }; + } } else { while (count--) { webkit_find_controller_search_previous(fc); - }; + } } } diff --git a/src/command.h b/src/command.h index ba2ba46b..16a5d25a 100644 --- a/src/command.h +++ b/src/command.h @@ -43,7 +43,7 @@ enum { }; #endif -gboolean command_search(Client *c, const Arg *arg); +gboolean command_search(Client *c, const Arg *arg, bool commit); gboolean command_yank(Client *c, const Arg *arg, char buf); gboolean command_save(Client *c, const Arg *arg); #ifdef FEATURE_QUEUE diff --git a/src/ex.c b/src/ex.c index 0949f553..7b154a55 100644 --- a/src/ex.c +++ b/src/ex.c @@ -402,8 +402,7 @@ void ex_input_changed(Client *c, const char *text) case '/': /* fall through */ case '?': if (c->config.incsearch) { - command_search(c, &((Arg){0, NULL})); /* stop last search */ - command_search(c, &((Arg){*text == '/' ? 1 : -1, (char*)text + 1})); + command_search(c, &((Arg){*text == '/' ? 1 : -1, (char*)text + 1}), FALSE); } break; } @@ -511,10 +510,7 @@ static void input_activate(Client *c) case '?': vb_enter(c, 'n'); - /* start search, if incsearch, it's done while typing */ - if (!c->config.incsearch) { - command_search(c, &((Arg){count, cmd})); - } + command_search(c, &((Arg){count, strlen(cmd) ? cmd : NULL}), TRUE); break; case ';': /* fall through */ diff --git a/src/main.c b/src/main.c index 8e38de20..9059a82d 100644 --- a/src/main.c +++ b/src/main.c @@ -1202,7 +1202,7 @@ static void on_webview_load_changed(WebKitWebView *webview, marks_clear(c); /* Unset possible last search. */ - command_search(c, &((Arg){0})); + command_search(c, &(Arg){0, NULL}, FALSE); break; diff --git a/src/main.h b/src/main.h index 36fac576..5124c99a 100644 --- a/src/main.h +++ b/src/main.h @@ -173,9 +173,9 @@ struct State { WebKitHitTestResult *hit_test_result; struct { - gboolean active; /* indicate if there is a acitve search */ + gboolean active; /* indicate if there is a active search */ short direction; /* last direction 1 forward, -1 backward */ - int matches; /* number of matches search results */ + int matches; /* number of matching search results */ } search; }; diff --git a/src/normal.c b/src/normal.c index 31cdaa4e..a951b6c9 100644 --- a/src/normal.c +++ b/src/normal.c @@ -227,7 +227,7 @@ void normal_enter(Client *c) */ void normal_leave(Client *c) { - command_search(c, &((Arg){0})); + command_search(c, &((Arg){0, NULL}), FALSE); } /** @@ -344,7 +344,7 @@ static VbResult normal_clear_input(Client *c, const NormalCmdInfo *info) vb_echo(c, MSG_NORMAL, FALSE, ""); /* Unset search highlightning. */ - command_search(c, &((Arg){0})); + command_search(c, &((Arg){0, NULL}), FALSE); return RESULT_COMPLETE; } @@ -728,7 +728,7 @@ static VbResult normal_search(Client *c, const NormalCmdInfo *info) { int count = (info->count > 0) ? info->count : 1; - command_search(c, &((Arg){info->key == 'n' ? count : -count})); + command_search(c, &((Arg){info->key == 'n' ? count : -count, NULL}), FALSE); return RESULT_COMPLETE; } @@ -747,10 +747,7 @@ static VbResult normal_search_selection(Client *c, const NormalCmdInfo *info) } count = (info->count > 0) ? info->count : 1; - /* stopp possible existing search and the search highlights before - * starting the new search query */ - command_search(c, &((Arg){0})); - command_search(c, &((Arg){info->key == '*' ? count : -count, query})); + command_search(c, &((Arg){info->key == '*' ? count : -count, query}), TRUE); g_free(query); return RESULT_COMPLETE; From 0a032555463f16e8d00389b087fbfec8b37050e2 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 24 Apr 2017 13:15:15 +0200 Subject: [PATCH 114/201] Don't run input change on stepping through completion. --- src/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main.c b/src/main.c index 9059a82d..dddb0f77 100644 --- a/src/main.c +++ b/src/main.c @@ -819,12 +819,10 @@ static void on_textbuffer_changed(GtkTextBuffer *textbuffer, gpointer user_data) g_assert(c); -#if 0 /* don't observe changes in completion mode */ if (c->mode->flags & FLAG_COMPLETION) { return; } -#endif /* don't process changes not typed by the user */ if (gtk_widget_is_focus(c->input) && c->mode && c->mode->input_changed) { From edd5bec6617d600617b7925ce3e811a7c43502a5 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 26 Apr 2017 22:13:36 +0200 Subject: [PATCH 115/201] Fixed wrongs check of g_mkdir_with_parents #381. --- src/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 8f1c2b45..b19aeaa5 100644 --- a/src/util.c +++ b/src/util.c @@ -101,7 +101,7 @@ void util_cleanup(void) gboolean util_create_dir_if_not_exists(const char *dirpath) { - if (!g_mkdir_with_parents(dirpath, 0755)) { + if (g_mkdir_with_parents(dirpath, 0755) == -1) { g_critical("Could not create directory '%s': %s", dirpath, strerror(errno)); return FALSE; From f4909eee293b18ed6609217138e0506b04fc2169 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 26 Apr 2017 23:43:24 +0200 Subject: [PATCH 116/201] Allow to store multiple closed URLs #379. Applied changes of 8ae4cadf58e46b3e9951fa89cf084bd2792984c3 in master branch to new infrastructure. --- doc/vimb.1 | 6 +++++- src/main.c | 26 ++++++++++++++++++++++++++ src/main.h | 1 + src/normal.c | 11 +++++++---- src/setting.c | 3 +++ 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 50297907..35316c80 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -644,6 +644,10 @@ 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. +.TP +.B closed-max-items (int) +Maximum number of stored last closed URLs. +If closed-max-items is set to 0, closed URLs will not be stored. .SH FILES .TP .IR $XDG_CONFIG_HOME/vimb[/PROFILE] @@ -660,7 +664,7 @@ Configuration file to set WebKit setting, some GUI styles and keybindings. Cookie store file. .TP .I closed -Holds the URI of last closed browser windows. +Holds the URIs of last closed browser windows. .TP .I history This file holds the history of unique opened URIs. diff --git a/src/main.c b/src/main.c index dddb0f77..f6d6c9a1 100644 --- a/src/main.c +++ b/src/main.c @@ -600,6 +600,32 @@ static void client_destroy(Client *c) { Client *p; webkit_web_view_stop_loading(c->webview); + + /* Write last URL into file for recreation. + * The URL is only stored if the closed-max-items is not 0 and the file + * exists. */ + if (c->state.uri && vb.config.closed_max && vb.files[FILES_CLOSED]) { + char **lines; + GString *new; + + /* The latest closed URL is written first and the surplus items are + * removed. */ + lines = util_get_lines(vb.files[FILES_CLOSED]); + new = g_string_new(c->state.uri); + g_string_append(new, "\n"); + if (lines) { + int len, i; + + len = g_strv_length(lines); + 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); + } + gtk_widget_destroy(c->window); /* Look for the client in the list, if we searched through the list and diff --git a/src/main.h b/src/main.h index 5124c99a..19a593cc 100644 --- a/src/main.h +++ b/src/main.h @@ -259,6 +259,7 @@ struct Vimb { char *profile; /* profile name */ struct { guint history_max; + guint closed_max; } config; GtkCssProvider *style_provider; }; diff --git a/src/normal.c b/src/normal.c index a951b6c9..5a438368 100644 --- a/src/normal.c +++ b/src/normal.c @@ -597,12 +597,15 @@ static VbResult normal_open_clipboard(Client *c, const NormalCmdInfo *info) static VbResult normal_open(Client *c, const NormalCmdInfo *info) { Arg a; - char *file; + if (!vb.files[FILES_CLOSED]) { + 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); + a.s = util_file_pop_line(vb.files[FILES_CLOSED], NULL); + if (!a.s) { + return RESULT_ERROR; + } vb_load_uri(c, &a); g_free(a.s); diff --git a/src/setting.c b/src/setting.c index f0752e8b..62261cf6 100644 --- a/src/setting.c +++ b/src/setting.c @@ -141,6 +141,9 @@ void setting_init(Client *c) setting_add(c, "default-zoom", TYPE_INTEGER, &i, default_zoom, 0, NULL); setting_add(c, "download-path", TYPE_CHAR, &"~/", NULL, 0, NULL); setting_add(c, "incsearch", TYPE_BOOLEAN, &off, internal, 0, &c->config.incsearch); + i = 10; + /* TODO should be global and not overwritten by a new client */ + setting_add(c, "closed-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.closed_max); #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT /* gui style settings vimb2 compatibility */ From 06bd9e1d4ffa992f5f0cd7d756e73a1309781526 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 00:13:22 +0200 Subject: [PATCH 117/201] Moved closed file writing to util #379. --- src/main.c | 21 ++------------------- src/util.c | 34 ++++++++++++++++++++++++++++++++++ src/util.h | 2 ++ 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/main.c b/src/main.c index f6d6c9a1..8a13d579 100644 --- a/src/main.c +++ b/src/main.c @@ -605,25 +605,8 @@ static void client_destroy(Client *c) * The URL is only stored if the closed-max-items is not 0 and the file * exists. */ if (c->state.uri && vb.config.closed_max && vb.files[FILES_CLOSED]) { - char **lines; - GString *new; - - /* The latest closed URL is written first and the surplus items are - * removed. */ - lines = util_get_lines(vb.files[FILES_CLOSED]); - new = g_string_new(c->state.uri); - g_string_append(new, "\n"); - if (lines) { - int len, i; - - len = g_strv_length(lines); - 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); + util_file_prepend_line(vb.files[FILES_CLOSED], c->state.uri, + vb.config.closed_max); } gtk_widget_destroy(c->window); diff --git a/src/util.c b/src/util.c index b19aeaa5..dd501044 100644 --- a/src/util.c +++ b/src/util.c @@ -206,6 +206,40 @@ gboolean util_file_prepend(const char *file, const char *format, ...) return res; } +/** + * Prepend a new line to the file and make sure there are not more than + * max_lines in the file. + * + * @file: File to prepend the data + * @line: Line to be written as new first line into the file. + * The line ending is inserted automatic. + * @max_lines Maximum number of lines in file after the operation. + */ +void util_file_prepend_line(const char *file, const char *line, + unsigned int max_lines) +{ + char **lines; + GString *new_content; + + g_assert(file); + g_assert(line); + + lines = util_get_lines(file); + /* Write first the new line into the string and append the new line. */ + new_content = g_string_new(line); + g_string_append(new_content, "\n"); + if (lines) { + int len, i; + + len = g_strv_length(lines); + for (i = 0; i < len - 1 && i < max_lines - 1; i++) { + g_string_append_printf(new_content, "%s\n", lines[i]); + } + g_strfreev(lines); + } + g_file_set_contents(file, new_content->str, -1, NULL); + g_string_free(new_content, TRUE); +} /** * Retrieves the first line from file and delete it from file. diff --git a/src/util.h b/src/util.h index f416e5c0..801654d1 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,8 @@ gboolean util_create_dir_if_not_exists(const char *dirpath); 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, ...); +void util_file_prepend_line(const char *file, const char *line, + unsigned int max_lines); char *util_file_pop_line(const char *file, int *item_count); char *util_get_config_dir(void); char *util_get_file_contents(const char *filename, gsize *length); From 1827869a2647cf9f066eacfeec82414096e62e8d Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 00:37:38 +0200 Subject: [PATCH 118/201] Add queue feature to manual page #365. --- doc/vimb.1 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 35316c80..6ecf47ed 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -114,6 +114,10 @@ Open the last closed page. .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. +.TP .BI [ \(dqx ]p Open the URI out of the register \fIx\fP or, if not given, from the clipboard. .TP @@ -499,6 +503,22 @@ Show the current set value of variable. .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. +This list is shared between the single instances of Vimb. +.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 Misc .TP .BI ":sh[ellcmd] " cmd From 63ce12fddac3dca1f8e2e4dd336d4b58dc007eff Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 00:40:28 +0200 Subject: [PATCH 119/201] Added missed queue file in manual page #365. --- doc/vimb.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 6ecf47ed..f8641c04 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -693,6 +693,9 @@ This file holds the history of unique opened URIs. This file holds the history of commands and search queries performed via input box. .TP +.I queue +Holds the read it later queue filled by `qpush'. +.TP .I search This file holds the history of search queries. .TP From a5b230ecd4ac50e8f62c2f6cad39a2831a0c6023 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 00:43:35 +0200 Subject: [PATCH 120/201] Added section for input mode to man page #365. --- doc/vimb.1 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index f8641c04..2297c6c7 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -627,6 +627,19 @@ Like :normal, but no mapping is applied to \fIcmds\fP. Print current document. Open a GUI dialog where you can select the printer, number of copies, orientation, etc. +.SH INPUT MODE +.TP +.B , 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\-V +Pass the next key press directly to WebKit. +.TP +.B CTRL\-Z +Enter the pass-through mode. .SH COMPLETIONS The completions are triggered by pressing `` or `` in the activated inputbox. From aedf17db09771100122503aae3a20f8d450f36ba Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 21:13:10 +0200 Subject: [PATCH 121/201] Add bookmark completion to manual page #365. --- doc/vimb.1 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 2297c6c7..22a0a91a 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -671,6 +671,27 @@ 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 is 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 From 43e4c5876d76b5a3183e25a5b0aa7e9c64899c97 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 21:32:48 +0200 Subject: [PATCH 122/201] Allow also completion for whitespace prefixed commands. We allow to put whitespace or additional ':' before ex commands to avoid recoding of the commands in the history. But in the completion only the first ':' was skipped to get the command to apply completion for. So ': open foo' did not start completion, because ' open' is none known command. So skip all ':' and whitespace after the first ':' to set the pointer to the beginning of the command. --- src/ex.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ex.c b/src/ex.c index 7b154a55..6ce1ecbc 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1129,12 +1129,13 @@ static gboolean complete(Client *c, short direction) in = (const char*)input; if (*in == ':') { const char *before_cmdname; - /* skipt the first : */ - in++; + /* skip leading ':' and whitespace */ + while (*in && (*in == ':' || VB_IS_SPACE(*in))) { + in++; + } ExArg *arg = g_slice_new0(ExArg); - skip_whitespace(&in); parse_count(&in, arg); /* Backup the current pointer so that we can restore the input pointer From ee218313a2d3f1c41f30102ba5498471b32925c0 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 22:08:17 +0200 Subject: [PATCH 123/201] Keep all content before command on completion. If ':: o' was completed, the white space and ':' before the command where skipped and the user input was changed into ':open'. Vim keeps the input also during command completion, so we follow this model and keep it too. --- src/ex.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ex.c b/src/ex.c index 6ce1ecbc..6a641912 100644 --- a/src/ex.c +++ b/src/ex.c @@ -801,7 +801,7 @@ static void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, Cli gboolean success = FALSE; char *string = NULL; - GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); + GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); if (return_value) { g_variant_get(return_value, "(bs)", &success, &string); if (success) { @@ -1139,7 +1139,7 @@ static gboolean complete(Client *c, short direction) parse_count(&in, arg); /* Backup the current pointer so that we can restore the input pointer - * if tha command name parsing fails. */ + * if the command name parsing fails. */ before_cmdname = in; /* Do ex command specific completion if the comman is recognized and @@ -1215,7 +1215,8 @@ static gboolean complete(Client *c, short direction) excomp.count = arg->count; if (ex_fill_completion(store, in)) { - OVERWRITE_STRING(excomp.prefix, ":"); + /* Use all the input before the command as prefix. */ + OVERWRITE_NSTRING(excomp.prefix, input, in - input); found = TRUE; } } From 9a48524c7e068ed3952206174fa5a2e8093ada6f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 22:16:04 +0200 Subject: [PATCH 124/201] Fix some typos. --- src/completion.c | 4 ++-- src/ex.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/completion.c b/src/completion.c index 8bda61f0..22fced68 100644 --- a/src/completion.c +++ b/src/completion.c @@ -205,8 +205,8 @@ gboolean completion_next(Client *c, gboolean back) comp->active--; /* Step back over the beginning. */ if (comp->active == -1) { - /* Unselect the current item to show the user that the shown - * content is the initial typed content. */ + /* Deselect the current item to show the user the initial typed + * content. */ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(comp->tree))); return FALSE; diff --git a/src/ex.c b/src/ex.c index 6a641912..11fd7b61 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1087,7 +1087,7 @@ static VbCmdResult ex_source(Client *c, const ExArg *arg) /** * Manage the generation and stepping through completions. * This function prepared some prefix and suffix string that are required to - * put hte matched data back to inputbox, and prepares the tree list store + * put the matched data back to inputbox, and prepares the tree list store * model containing matched values. */ static gboolean complete(Client *c, short direction) @@ -1142,7 +1142,7 @@ static gboolean complete(Client *c, short direction) * if the command name parsing fails. */ before_cmdname = in; - /* Do ex command specific completion if the comman is recognized and + /* Do ex command specific completion if the command is recognized and * there is a space after the command and the optional '!' bang. */ if (parse_command_name(c, &in, arg) && parse_bang(&in, arg) && VB_IS_SPACE(*in)) { const char *token; From 48e55c8fc865995c354086a275100da947fb3500 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 27 Apr 2017 23:22:33 +0200 Subject: [PATCH 125/201] Changed default-zoom behaviour. In the previous implementation the default-zoom was only a initial full content zoom applied to the webview. But in case the user changed the zooming and reset it back by using 'zz' the webkit zoom level was set to 1.0 (100%) and not to the initial zoom level like on startup of vimb. This behaviour is strange to under stand and does not fit toe the setting name 'default-zoom'. To make the default-zoom to a real default, the zoom is also applied in case the zoom is reseted by the user via 'zz'. --- doc/vimb.1 | 3 +++ src/main.h | 1 + src/normal.c | 6 ++++-- src/setting.c | 6 ++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 22a0a91a..fa2f0431 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -702,6 +702,9 @@ All settings listed below can be set with the `:set' command. .B closed-max-items (int) Maximum number of stored last closed URLs. If closed-max-items is set to 0, closed URLs will not be stored. +.TP +.B default-zoom (int) +Default Full-Content zoom level in percent. Default is 100. .SH FILES .TP .IR $XDG_CONFIG_HOME/vimb[/PROFILE] diff --git a/src/main.h b/src/main.h index 19a593cc..6ff37f44 100644 --- a/src/main.h +++ b/src/main.h @@ -230,6 +230,7 @@ struct Client { GdkRGBA comp_fg[COMP_LAST]; GdkRGBA comp_bg[COMP_LAST]; PangoFontDescription *comp_font; + guint default_zoom; /* default zoom level in percent */ } config; struct { GSList *list; diff --git a/src/normal.c b/src/normal.c index 5a438368..156ef52a 100644 --- a/src/normal.c +++ b/src/normal.c @@ -805,8 +805,10 @@ static VbResult normal_zoom(Client *c, const NormalCmdInfo *info) count = info->count ? (float)info->count : 1.0; - if (info->key2 == 'z') { /* zz reset zoom */ - webkit_web_view_set_zoom_level(view, 1.0); + /* zz reset zoom to it's default zoom level */ + if (info->key2 == 'z') { + webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(view), FALSE); + webkit_web_view_set_zoom_level(view, c->config.default_zoom / 100.0); return RESULT_COMPLETE; } diff --git a/src/setting.c b/src/setting.c index 62261cf6..6b9cee23 100644 --- a/src/setting.c +++ b/src/setting.c @@ -507,10 +507,12 @@ static int cookie_accept(Client *c, const char *name, DataType type, void *value static int default_zoom(Client *c, const char *name, DataType type, void *value, void *data) { - float zoom = (float)*(int*)value / 100.0; + /* Store the percent value in the client config. */ + c->config.default_zoom = *(int*)value; + /* Apply the default zoom to the webview. */ webkit_settings_set_zoom_text_only(webkit_web_view_get_settings(c->webview), FALSE); - webkit_web_view_set_zoom_level(c->webview, zoom); + webkit_web_view_set_zoom_level(c->webview, c->config.default_zoom / 100.0); return CMD_SUCCESS; } From a4824b95d144fda7c8caf197c5af19f8a5558a34 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 4 May 2017 19:40:10 +0200 Subject: [PATCH 126/201] fixes download file name postfix (closes #383) --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 8a13d579..dccd867e 100644 --- a/src/main.c +++ b/src/main.c @@ -163,7 +163,7 @@ gboolean vb_download_set_destination(Client *c, WebKitDownload *download, /* Construct a new complete odwnload filepath with suffic before the * file extension. */ do { - num = g_strdup_printf("%d", i++); + num = g_strdup_printf("_%d", i++); g_string_assign(tmp, file); g_string_insert(tmp, suffix, num); g_free(num); From a8200bb868df522055125cf95427655833b26870 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 4 May 2017 19:42:50 +0200 Subject: [PATCH 127/201] fixes typos --- src/main.c | 2 +- src/util.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 8a13d579..20d397fa 100644 --- a/src/main.c +++ b/src/main.c @@ -160,7 +160,7 @@ gboolean vb_download_set_destination(Client *c, WebKitDownload *download, tmp = g_string_new(NULL); num = g_strdup_printf("%d", i++); - /* Construct a new complete odwnload filepath with suffic before the + /* Construct a new complete download filepath with suffix before the * file extension. */ do { num = g_strdup_printf("%d", i++); diff --git a/src/util.c b/src/util.c index dd501044..f91dd892 100644 --- a/src/util.c +++ b/src/util.c @@ -53,7 +53,7 @@ char *util_build_path(Client *c, const char *path, const char *dir) if ((fexp = util_expand(c, 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 + * no need to free fexp, because this should be done by the caller * on fullPath later */ fullPath = fexp; } else if (dir && *dir) { From b622cdf534a74053692356da1aea493c1f1882ac Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Sun, 23 Apr 2017 21:10:37 +0200 Subject: [PATCH 128/201] Reject window manager quit if downloading Rejects a window manager initiated quit signal if there are downloads in progress. This closes #375. --- src/main.c | 22 +++++++++++++++++++--- src/main.h | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 8a13d579..e58200ef 100644 --- a/src/main.c +++ b/src/main.c @@ -80,6 +80,7 @@ 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 gboolean on_window_delete_event(GtkWidget *window, GdkEvent *event, Client *c); static void on_window_destroy(GtkWidget *window, Client *c); static gboolean quit(Client *c); static void read_from_stdin(Client *c); @@ -447,17 +448,19 @@ void vb_modelabel_update(Client *c, const char *label) /** * Close the given client instances window. */ -void vb_quit(Client *c, gboolean force) +gboolean vb_quit(Client *c, gboolean force) { /* if not forced quit - don't quit if there are still running downloads */ if (!force && c->state.downloads) { - vb_echo_force(c, MSG_ERROR, TRUE, "Can't quit: there are running downloads"); - return; + vb_echo_force(c, MSG_ERROR, TRUE, "Can't quit: there are running downloads. Use :q! to force quit"); + return FALSE; } /* 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); + + return TRUE; } /** @@ -673,6 +676,7 @@ static Client *client_new(WebKitWebView *webview, gboolean show) g_object_connect( G_OBJECT(c->window), "signal::destroy", G_CALLBACK(on_window_destroy), c, + "signal::delete-event", G_CALLBACK(on_window_delete_event), c, "signal::key-press-event", G_CALLBACK(on_map_keypress), c, NULL); @@ -1309,6 +1313,18 @@ static gboolean on_webview_web_process_crashed(WebKitWebView *webview, Client *c return TRUE; } +/** + * Callback for window ::delete-event signal which is emitted if a user + * requests that a toplevel window is closed. The default handler for this + * signal destroys the window. Returns TRUE to stop other handlers from being + * invoked for the event. FALSE to propagate the event further. + */ +static gboolean on_window_delete_event(GtkWidget *window, GdkEvent *event, Client *c) +{ + /* if vb_quit fails, do not propagate event further, keep window open */ + return !vb_quit(c, FALSE); +} + /** * Callback for the window destroy signal. * Destroys the client that is associated to the window. diff --git a/src/main.h b/src/main.h index 6ff37f44..38f88080 100644 --- a/src/main.h +++ b/src/main.h @@ -279,7 +279,7 @@ void vb_mode_add(char id, ModeTransitionFunc enter, ModeTransitionFunc leave, ModeKeyFunc keypress, ModeInputChangedFunc input_changed); 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); +gboolean 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); From ed4b63cbc6e677fa8d1be009985d175212f60af3 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 6 May 2017 01:55:27 +0200 Subject: [PATCH 129/201] Don't set empty title is none is found. In case a page without a title or a none html file which can be shown is opened, webkit informed us about the title change to empty string. But no window title is no good idea, especial in case vimb runs within tabbed and there are multiple unnamed tabs. So we set the webview title only if it's not empty. --- src/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 8a13d579..6d3188fb 100644 --- a/src/main.c +++ b/src/main.c @@ -1276,7 +1276,11 @@ static void on_webview_notify_estimated_load_progress(WebKitWebView *webview, */ static void on_webview_notify_title(WebKitWebView *webview, GParamSpec *pspec, Client *c) { - set_title(c, webkit_web_view_get_title(webview)); + const char *title = webkit_web_view_get_title(webview); + + if (*title) { + set_title(c, title); + } } /** From 35a2acc455078b056803d2af1a7598d929f35d53 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sun, 23 Apr 2017 20:53:34 -0400 Subject: [PATCH 130/201] Very preliminary work for bringing hints back --- src/main.c | 8 + src/normal.c | 4 +- src/scripts/hints.js | 545 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 src/scripts/hints.js diff --git a/src/main.c b/src/main.c index 7ab65726..cace512d 100644 --- a/src/main.c +++ b/src/main.c @@ -1652,6 +1652,7 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) { WebKitWebView *new; WebKitUserContentManager *ucm; + WebKitUserScript *script; /* create a new webview */ if (webview) { @@ -1679,6 +1680,13 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) g_signal_connect(webkit_web_context_get_default(), "download-started", G_CALLBACK(on_webctx_download_started), c); + /* Inject the global hints script. */ + script = webkit_user_script_new(HINTS, + 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); + /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); diff --git a/src/normal.c b/src/normal.c index 156ef52a..1dc32615 100644 --- a/src/normal.c +++ b/src/normal.c @@ -408,7 +408,9 @@ static VbResult normal_ex(Client *c, const NormalCmdInfo *info) if (info->key == 'F') { vb_enter_prompt(c, 'c', ";t", TRUE); } else if (info->key == 'f') { - vb_enter_prompt(c, 'c', ";o", TRUE); + g_print("firing!"); + ext_proxy_eval_script(c, "testHint();", NULL); + /* vb_enter_prompt(c, 'c', ";o", TRUE); */ } else { char prompt[2] = {info->key, '\0'}; vb_enter_prompt(c, 'c', prompt, TRUE); diff --git a/src/scripts/hints.js b/src/scripts/hints.js new file mode 100644 index 00000000..7f4dcf21 --- /dev/null +++ b/src/scripts/hints.js @@ -0,0 +1,545 @@ +var hints = 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.getClientRects()[0]; + 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 + ); + } + + 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, + }; +})()); + +function testHint() { + console.log("harmless testing!"); + /* hints.init('t', false, 500, '', false, false); */ +} From d01d8b589c996ad2c6d7997b3925e7900725b1c0 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sun, 23 Apr 2017 22:47:22 -0400 Subject: [PATCH 131/201] Add hint display Pressing 'F' will show hints with the old styling from the master branch. --- src/config.def.h | 32 ++++++++++++++++++++++++++++++++ src/scripts/hints.js | 2 +- src/scripts/js2h.sh | 3 ++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/config.def.h b/src/config.def.h index 2682a9d9..95e8970d 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -50,3 +50,35 @@ #define SETTING_GUI_FONT_NORMAL "10pt monospace" #define SETTING_GUI_FONT_EMPH "bold 10pt monospace" #define SETTING_HOME_PAGE "about:blank" + +/* 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;\ +transition:all 0 !important;\ +transition-delay:all 0 !important\ +}\ +._hintElem._hintFocus{\ +background-color:#8f0 !important\ +}\ +._hintLabel._hintFocus{\ +z-index:100001;\ +opacity:1\ +}" + diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 7f4dcf21..04cd51c3 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -541,5 +541,5 @@ var hints = Object.freeze((function(){ function testHint() { console.log("harmless testing!"); - /* hints.init('t', false, 500, '', false, false); */ + hints.init('t', false, 500, '0123456789', false, false); } diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index 82903d55..4a76777f 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -32,7 +32,8 @@ sed -e 's:/\*[^*]*\*/::g' \ # ecaspe sed -e 's|\\x20| |g' \ -e 's|\\|\\\\|g' \ - -e 's|"|\\"|g' | \ + -e 's|"|\\"|g' \ + -e 's|HINT_CSS|" HINT_CSS "|' | \ # write opener with the starting and ending quote char sed -e "1s/^/#define $CONSTANT \"/" \ -e '$s/$/"\n/' From ee115b3edf9ca4f59bc3643949297e1783fd5f68 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 26 Apr 2017 22:54:30 -0400 Subject: [PATCH 132/201] Convert old hints.c to the webkit2 era Lots of it is still disabled, but the basic hint showing happening when pressing 'f' works. --- src/config.def.h | 2 + src/ex.c | 7 +- src/hints.c | 379 +++++++++++++++++++++++++++++++++++++++++++ src/hints.h | 34 ++++ src/normal.c | 4 +- src/scripts/hints.js | 5 - src/setting.c | 4 + 7 files changed, 423 insertions(+), 12 deletions(-) create mode 100644 src/hints.c create mode 100644 src/hints.h diff --git a/src/config.def.h b/src/config.def.h index 95e8970d..0e821370 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -51,6 +51,8 @@ #define SETTING_GUI_FONT_EMPH "bold 10pt monospace" #define SETTING_HOME_PAGE "about:blank" +#define MAXIMUM_HINTS 500 + /* 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{\ diff --git a/src/ex.c b/src/ex.c index 11fd7b61..39cc60b9 100644 --- a/src/ex.c +++ b/src/ex.c @@ -33,6 +33,7 @@ #include "config.h" #include "ex.h" #include "handler.h" +#include "hints.h" #include "history.h" #include "main.h" #include "map.h" @@ -225,9 +226,7 @@ void ex_enter(Client *c) void ex_leave(Client *c) { completion_clean(c); -#if 0 - hints_clear(); -#endif + hints_clear(c); } /** @@ -397,7 +396,7 @@ void ex_input_changed(Client *c, const char *text) switch (*text) { case ';': /* fall through - the modes are handled by hints_create */ case 'g': - /* TODO create hints */ + hints_create(c, text); break; case '/': /* fall through */ case '?': diff --git a/src/hints.c b/src/hints.c new file mode 100644 index 00000000..1792120c --- /dev/null +++ b/src/hints.c @@ -0,0 +1,379 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2017 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 +#include +#include +#include +#include "hints.h" +#include "main.h" +#include "ascii.h" +#include "command.h" +#include "hints.js.h" +#include "input.h" +#include "map.h" +#include "ext-proxy.h" + +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; + /* holds the setting if JavaScript can open windows automatically that we + * have to change to open windows via hinting */ + gboolean allow_open_win; + guint timeout_id; +} hints; + +extern struct Vimb vb; + +static gboolean call_hints_function(Client *c, const char *func, int count, JSValueRef params[]); +static void fire_timeout(Client *c, gboolean on); +static gboolean fire_cb(gpointer data); + + +VbResult hints_keypress(Client *c, int key) +{ + JSValueRef arguments[1]; + + if (key == KEY_CR) { + hints_fire(c); + + return RESULT_COMPLETE; + } else if (key == CTRL('H')) { + fire_timeout(c, false); + arguments[0] = JSValueMakeNull(hints.ctx); + if (call_hints_function(c, "update", 1, arguments)) { + return RESULT_COMPLETE; + } + } else if (key == KEY_TAB) { + fire_timeout(c, false); + hints_focus_next(c, false); + + return RESULT_COMPLETE; + } else if (key == KEY_SHIFT_TAB) { + fire_timeout(c, false); + hints_focus_next(c, true); + + return RESULT_COMPLETE; + } else { + fire_timeout(c, true); + /* try to handle the key by the javascript */ + /* arguments[0] = js_string_to_ref(hints.ctx, (char[]){key, '\0'}); */ + /* if (call_hints_function(c, "update", 1, arguments)) { */ + /* return RESULT_COMPLETE; */ + /* } */ + } + + fire_timeout(c, false); + return RESULT_ERROR; +} + +void hints_clear(Client *c) +{ + if (c->mode->flags & FLAG_HINTING) { + c->mode->flags &= ~FLAG_HINTING; + vb_input_set_text(c, ""); + + call_hints_function(c, "clear", 0, NULL); + + g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); + + /* if open window was not allowed for JavaScript, restore this */ + /* if (!hints.allow_open_win) { */ + /* WebKitWebSettings *setting = webkit_web_view_get_settings(c->webview); */ + /* g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL); */ + /* } */ + } +} + +void hints_create(Client *c, const char *input) +{ + char *jscode; + + /* 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 (c->mode->flags & FLAG_HINTING) { + vb_enter(c, 'n'); + } + return; + } + + if (!(c->mode->flags & FLAG_HINTING)) { + c->mode->flags |= FLAG_HINTING; + + /* WebKitWebSettings *setting = webkit_web_view_get_settings(c->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); */ + /* } */ + + hints.promptlen = hints.gmode ? 3 : 2; + + jscode = g_strdup_printf("hints.init('%s', %s, %d, '%s', %s, %s);", + (char[]){hints.mode, '\0'}, + hints.gmode ? "true" : "false", + MAXIMUM_HINTS, + GET_CHAR(c, "hintkeys"), + GET_BOOL(c, "hint-follow-last") ? "true" : "false", + GET_BOOL(c, "hint-number-same-length") ? "true" : "false" + ); + + ext_proxy_eval_script(c, jscode, NULL); + g_free(jscode); + + /* 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(c, "filter", 1, arguments); */ +} + +void hints_focus_next(Client *c, const gboolean back) +{ + JSValueRef arguments[] = { + JSValueMakeNumber(hints.ctx, back) + }; + call_hints_function(c, "focus", 1, arguments); +} + +void hints_fire(Client *c) +{ + call_hints_function(c, "fire", 0, NULL); +} + +void hints_follow_link(Client *c, 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(c, "followLink", 3, arguments); */ +} + +void hints_increment_uri(Client *c, int count) +{ + JSValueRef arguments[] = { + JSValueMakeNumber(hints.ctx, count) + }; + + call_hints_function(c, "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(Client *c, const char *func, int count, JSValueRef params[]) +{ + /* char *value = js_object_call_function(hints.ctx, hints.obj, func, count, params); */ + char *value = ""; + return true; + + 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( + c->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(c, 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 && c->mode->id == 'c') { + vb_enter(c, 'n'); + } + } else if (!strncmp(value, "INSERT:", 7)) { + fire_timeout(c, false); + vb_enter(c, 'i'); + if (hints.mode == 'e') { + input_open_editor(c); + } + } else if (!strncmp(value, "DATA:", 5)) { + fire_timeout(c, 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(c, 'n'); + } + + char *v = (value + 5); + Arg a = {0}; + /* put the hinted value into register "; */ + vb_register_add(c, ';', v); + switch (hints.mode) { + /* used if images should be opened */ + case 'i': + case 'I': + a.s = v; + a.i = (hints.mode == 'I') ? TARGET_NEW : TARGET_CURRENT; + vb_load_uri(c, &a); + break; + + case 'O': + case 'T': + vb_echo(c, MSG_NORMAL, false, "%s %s", (hints.mode == 'T') ? ":tabopen" : ":open", v); + if (!hints.gmode) { + vb_enter(c, 'c'); + } + break; + + case 's': + a.s = v; + a.i = COMMAND_SAVE_URI; + command_save(c, &a); + break; + + case 'x': + map_handle_string(c, GET_CHAR(c, "x-hint-command"), true); + break; + + case 'y': + case 'Y': + a.i = COMMAND_YANK_ARG; + a.s = v; + command_yank(c, &a, c->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(c, &a); + break; +#endif + } + } + g_free(value); + return true; +} + +static void fire_timeout(Client *c, 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(c, "hint-timeout"); + if (millis) { + hints.timeout_id = g_timeout_add(millis, (GSourceFunc)fire_cb, c); + } + } +} + +static gboolean fire_cb(gpointer data) +{ + hints_fire(data); + + /* 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 new file mode 100644 index 00000000..6821554c --- /dev/null +++ b/src/hints.h @@ -0,0 +1,34 @@ +/** + * vimb - a webkit based vim like browser. + * + * Copyright (C) 2012-2016 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" + +VbResult hints_keypress(Client *c, int key); +void hints_create(Client *c, const char *input); +void hints_fire(Client *c); +void hints_follow_link(Client *c, gboolean back, int count); +void hints_increment_uri(Client *c, int count); +gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode); +void hints_clear(Client *c); +void hints_focus_next(Client *c, const gboolean back); + +#endif /* end of include guard: _HINTS_H */ diff --git a/src/normal.c b/src/normal.c index 1dc32615..156ef52a 100644 --- a/src/normal.c +++ b/src/normal.c @@ -408,9 +408,7 @@ static VbResult normal_ex(Client *c, const NormalCmdInfo *info) if (info->key == 'F') { vb_enter_prompt(c, 'c', ";t", TRUE); } else if (info->key == 'f') { - g_print("firing!"); - ext_proxy_eval_script(c, "testHint();", NULL); - /* vb_enter_prompt(c, 'c', ";o", TRUE); */ + vb_enter_prompt(c, 'c', ";o", TRUE); } else { char prompt[2] = {info->key, '\0'}; vb_enter_prompt(c, 'c', prompt, TRUE); diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 04cd51c3..6f3530f2 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -538,8 +538,3 @@ var hints = Object.freeze((function(){ focus: focus, }; })()); - -function testHint() { - console.log("harmless testing!"); - hints.init('t', false, 500, '0123456789', false, false); -} diff --git a/src/setting.c b/src/setting.c index 6b9cee23..a64f0dea 100644 --- a/src/setting.c +++ b/src/setting.c @@ -87,6 +87,10 @@ void setting_init(Client *c) 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, "hint-timeout", TYPE_INTEGER, &i, NULL, 0, NULL); + setting_add(c, "hintkeys", TYPE_CHAR, &"0123456789", NULL, 0, NULL); + setting_add(c, "hint-follow-last", TYPE_BOOLEAN, &on, NULL, 0, NULL); + setting_add(c, "hint-number-same-length", TYPE_BOOLEAN, &off, NULL, 0, NULL); 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"); From fb2c77b2755da7367ebbcbff7ebbb683031d5785 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 28 Apr 2017 22:02:03 -0400 Subject: [PATCH 133/201] hints: Re-enable nearly all call_hint_function invocations --- src/ex.c | 7 ++++++- src/hints.c | 55 ++++++++++++++++++++++++----------------------------- src/hints.h | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/ex.c b/src/ex.c index 39cc60b9..f558e4c3 100644 --- a/src/ex.c +++ b/src/ex.c @@ -241,7 +241,12 @@ VbResult ex_keypress(Client *c, int key) VbResult res; const char *text; - /* TODO delegate call to hint mode if this is active */ + /* delegate call to hint mode if this is active */ + if (c->mode->flags & FLAG_HINTING + && RESULT_COMPLETE == hints_keypress(c, key)) { + + return RESULT_COMPLETE; + } /* process the register */ if (info.phase == PHASE_REG) { diff --git a/src/hints.c b/src/hints.c index 1792120c..c5e8346a 100644 --- a/src/hints.c +++ b/src/hints.c @@ -45,25 +45,21 @@ static struct { extern struct Vimb vb; -static gboolean call_hints_function(Client *c, const char *func, int count, JSValueRef params[]); +static gboolean call_hints_function(Client *c, const char *func, char* args); static void fire_timeout(Client *c, gboolean on); static gboolean fire_cb(gpointer data); VbResult hints_keypress(Client *c, int key) { - JSValueRef arguments[1]; - if (key == KEY_CR) { hints_fire(c); return RESULT_COMPLETE; } else if (key == CTRL('H')) { fire_timeout(c, false); - arguments[0] = JSValueMakeNull(hints.ctx); - if (call_hints_function(c, "update", 1, arguments)) { - return RESULT_COMPLETE; - } + call_hints_function(c, "update", "null"); + return RESULT_MORE; // continue handling the backspace } else if (key == KEY_TAB) { fire_timeout(c, false); hints_focus_next(c, false); @@ -77,10 +73,8 @@ VbResult hints_keypress(Client *c, int key) } else { fire_timeout(c, true); /* try to handle the key by the javascript */ - /* arguments[0] = js_string_to_ref(hints.ctx, (char[]){key, '\0'}); */ - /* if (call_hints_function(c, "update", 1, arguments)) { */ - /* return RESULT_COMPLETE; */ - /* } */ + call_hints_function(c, "update", (char[]){key, '\0'}); + return RESULT_COMPLETE; } fire_timeout(c, false); @@ -93,7 +87,7 @@ void hints_clear(Client *c) c->mode->flags &= ~FLAG_HINTING; vb_input_set_text(c, ""); - call_hints_function(c, "clear", 0, NULL); + call_hints_function(c, "clear", ""); g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); @@ -105,9 +99,9 @@ void hints_clear(Client *c) } } -void hints_create(Client *c, const char *input) +void hints_create(Client *c, char *input) { - char *jscode; + char *jsargs; /* check if the input contains a valid hinting prompt */ if (!hints_parse_prompt(input, &hints.mode, &hints.gmode)) { @@ -134,7 +128,7 @@ void hints_create(Client *c, const char *input) hints.promptlen = hints.gmode ? 3 : 2; - jscode = g_strdup_printf("hints.init('%s', %s, %d, '%s', %s, %s);", + jsargs = g_strdup_printf("'%s', %s, %d, '%s', %s, %s", (char[]){hints.mode, '\0'}, hints.gmode ? "true" : "false", MAXIMUM_HINTS, @@ -143,29 +137,26 @@ void hints_create(Client *c, const char *input) GET_BOOL(c, "hint-number-same-length") ? "true" : "false" ); - ext_proxy_eval_script(c, jscode, NULL); - g_free(jscode); + call_hints_function(c, "init", jsargs); + g_free(jsargs); /* 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(c, "filter", 1, arguments); */ + jsargs = *(input + hints.promptlen) ? input + hints.promptlen : ""; + call_hints_function(c, "filter", jsargs); } void hints_focus_next(Client *c, const gboolean back) { - JSValueRef arguments[] = { - JSValueMakeNumber(hints.ctx, back) - }; - call_hints_function(c, "focus", 1, arguments); + call_hints_function(c, "focus", back ? "true" : "false"); } void hints_fire(Client *c) { - call_hints_function(c, "fire", 0, NULL); + call_hints_function(c, "fire", ""); } void hints_follow_link(Client *c, const gboolean back, int count) @@ -187,11 +178,11 @@ void hints_follow_link(Client *c, const gboolean back, int count) void hints_increment_uri(Client *c, int count) { - JSValueRef arguments[] = { - JSValueMakeNumber(hints.ctx, count) - }; + char *jsargs; - call_hints_function(c, "incrementUri", 1, arguments); + jsargs = g_strdup_printf("%d", count); + call_hints_function(c, "incrementUri", jsargs); + g_free(jsargs); } /** @@ -253,9 +244,13 @@ gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode) return res; } -static gboolean call_hints_function(Client *c, const char *func, int count, JSValueRef params[]) +static gboolean call_hints_function(Client *c, const char *func, char* args) { - /* char *value = js_object_call_function(hints.ctx, hints.obj, func, count, params); */ + char *jscode; + + jscode = g_strdup_printf("hints.%s(%s);", func, args); + ext_proxy_eval_script(c, jscode, NULL); + g_free(jscode); char *value = ""; return true; diff --git a/src/hints.h b/src/hints.h index 6821554c..1b23e86e 100644 --- a/src/hints.h +++ b/src/hints.h @@ -23,7 +23,7 @@ #include "main.h" VbResult hints_keypress(Client *c, int key); -void hints_create(Client *c, const char *input); +void hints_create(Client *c, char *input); void hints_fire(Client *c); void hints_follow_link(Client *c, gboolean back, int count); void hints_increment_uri(Client *c, int count); From 8da5f2aaa7a151cebd15db8a8834e12834b1814a Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 28 Apr 2017 22:24:37 -0400 Subject: [PATCH 134/201] hints: re-enable hints function return value processing --- src/hints.c | 51 +++++++++++++++++++++++++++++++-------------------- src/setting.c | 1 + 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/hints.c b/src/hints.c index c5e8346a..3cd9e5f0 100644 --- a/src/hints.c +++ b/src/hints.c @@ -45,7 +45,7 @@ static struct { extern struct Vimb vb; -static gboolean call_hints_function(Client *c, const char *func, char* args); +static void call_hints_function(Client *c, const char *func, char* args); static void fire_timeout(Client *c, gboolean on); static gboolean fire_cb(gpointer data); @@ -89,7 +89,7 @@ void hints_clear(Client *c) call_hints_function(c, "clear", ""); - g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); + /* g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); */ /* if open window was not allowed for JavaScript, restore this */ /* if (!hints.allow_open_win) { */ @@ -244,30 +244,32 @@ gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode) return res; } -static gboolean call_hints_function(Client *c, const char *func, char* args) +static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Client *c) { - char *jscode; + gboolean success = FALSE; + char *value = NULL; - jscode = g_strdup_printf("hints.%s(%s);", func, args); - ext_proxy_eval_script(c, jscode, NULL); - g_free(jscode); - char *value = ""; - return true; + g_print("callback!\n"); + GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); + if (!return_value) { + return; + } - g_return_val_if_fail(value != NULL, false); + g_variant_get(return_value, "(bs)", &success, &value); + if (!success) { + return; + } + g_print("foo! %s\n", value); if (!strncmp(value, "ERROR:", 6)) { - g_free(value); - return false; + return; } if (!strncmp(value, "OVER:", 5)) { - g_signal_emit_by_name( - c->webview, "hovering-over-link", NULL, *(value + 5) == '\0' ? NULL : (value + 5) - ); - g_free(value); - - return true; + /* g_signal_emit_by_name( */ + /* c->webview, "hovering-over-link", NULL, *(value + 5) == '\0' ? NULL : (value + 5) */ + /* ); */ + return; } /* following return values mark fired hints */ @@ -342,8 +344,16 @@ static gboolean call_hints_function(Client *c, const char *func, char* args) #endif } } - g_free(value); - return true; +} + +static void call_hints_function(Client *c, const char *func, char* args) +{ + char *jscode; + + jscode = g_strdup_printf("hints.%s(%s);", func, args); + ext_proxy_eval_script(c, jscode, (GAsyncReadyCallback)hints_function_callback); + g_free(jscode); + } static void fire_timeout(Client *c, gboolean on) @@ -357,6 +367,7 @@ static void fire_timeout(Client *c, gboolean on) if (on) { millis = GET_INT(c, "hint-timeout"); + g_print("millis %d", millis); if (millis) { hints.timeout_id = g_timeout_add(millis, (GSourceFunc)fire_cb, c); } diff --git a/src/setting.c b/src/setting.c index a64f0dea..92b0f42a 100644 --- a/src/setting.c +++ b/src/setting.c @@ -87,6 +87,7 @@ void setting_init(Client *c) 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"); + i = 1000; setting_add(c, "hint-timeout", TYPE_INTEGER, &i, NULL, 0, NULL); setting_add(c, "hintkeys", TYPE_CHAR, &"0123456789", NULL, 0, NULL); setting_add(c, "hint-follow-last", TYPE_BOOLEAN, &on, NULL, 0, NULL); From 81f543df5763f8efbfe7a20ad02fc1db8a9d61e4 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Mon, 1 May 2017 20:34:53 -0400 Subject: [PATCH 135/201] Make hints wotk in "new window" mode The old "set target to _blank" trick doesn't work anymore since the whole "noopener" vulnerability thing (https://mathiasbynens.github.io/rel-noopener/) I couldn't find a workaround. We're unfortunately now limited in the kind of elements we can interact with in "new window" mode. --- src/hints.c | 54 ++++++++++++++++++++------------------------ src/hints.h | 2 +- src/normal.c | 2 +- src/scripts/hints.js | 35 ++++++++++++---------------- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/src/hints.c b/src/hints.c index 3cd9e5f0..8a5f9212 100644 --- a/src/hints.c +++ b/src/hints.c @@ -19,6 +19,7 @@ #include "config.h" #include +#include #include #include #include @@ -32,11 +33,9 @@ #include "ext-proxy.h" 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; /* holds the setting if JavaScript can open windows automatically that we * have to change to open windows via hinting */ gboolean allow_open_win; @@ -45,7 +44,7 @@ static struct { extern struct Vimb vb; -static void call_hints_function(Client *c, const char *func, char* args); +static void call_hints_function(Client *c, const char *func, const char* args); static void fire_timeout(Client *c, gboolean on); static gboolean fire_cb(gpointer data); @@ -89,17 +88,15 @@ void hints_clear(Client *c) call_hints_function(c, "clear", ""); - /* g_signal_emit_by_name(c->webview, "hovering-over-link", NULL, NULL); */ - /* if open window was not allowed for JavaScript, restore this */ - /* if (!hints.allow_open_win) { */ - /* WebKitWebSettings *setting = webkit_web_view_get_settings(c->webview); */ - /* g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL); */ - /* } */ + if (!hints.allow_open_win) { + WebKitSettings *setting = webkit_web_view_get_settings(c->webview); + g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", hints.allow_open_win, NULL); + } } } -void hints_create(Client *c, char *input) +void hints_create(Client *c, const char *input) { char *jsargs; @@ -115,16 +112,16 @@ void hints_create(Client *c, char *input) if (!(c->mode->flags & FLAG_HINTING)) { c->mode->flags |= FLAG_HINTING; - /* WebKitWebSettings *setting = webkit_web_view_get_settings(c->webview); */ + WebKitSettings *setting = webkit_web_view_get_settings(c->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); */ + 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); */ - /* } */ + if (!hints.allow_open_win) { + g_object_set(G_OBJECT(setting), "javascript-can-open-windows-automatically", true, NULL); + } hints.promptlen = hints.gmode ? 3 : 2; @@ -133,7 +130,7 @@ void hints_create(Client *c, char *input) hints.gmode ? "true" : "false", MAXIMUM_HINTS, GET_CHAR(c, "hintkeys"), - GET_BOOL(c, "hint-follow-last") ? "true" : "false", + GET_BOOL(c, "hint-follow-last") ? "true" : "false", GET_BOOL(c, "hint-number-same-length") ? "true" : "false" ); @@ -145,8 +142,10 @@ void hints_create(Client *c, char *input) return; } - jsargs = *(input + hints.promptlen) ? input + hints.promptlen : ""; - call_hints_function(c, "filter", jsargs); + call_hints_function( + c, "filter", + *(input + hints.promptlen) ? input + hints.promptlen : "" + ); } void hints_focus_next(Client *c, const gboolean back) @@ -161,6 +160,11 @@ void hints_fire(Client *c) void hints_follow_link(Client *c, const gboolean back, int count) { + /* TODO implement outside of hints.c */ + /* We would previously "piggyback" on hints.js for the "js" part of this feature + * but this would actually be more elegant in its own JS file. This has nothing + * to do with hints. + */ /* char *json = g_strdup_printf( */ /* "[%s]", */ /* back ? vb.config.prevpattern : vb.config.nextpattern */ @@ -249,8 +253,7 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli gboolean success = FALSE; char *value = NULL; - g_print("callback!\n"); - GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); + GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); if (!return_value) { return; } @@ -259,19 +262,11 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli if (!success) { return; } - g_print("foo! %s\n", value); if (!strncmp(value, "ERROR:", 6)) { return; } - if (!strncmp(value, "OVER:", 5)) { - /* g_signal_emit_by_name( */ - /* c->webview, "hovering-over-link", NULL, *(value + 5) == '\0' ? NULL : (value + 5) */ - /* ); */ - return; - } - /* following return values mark fired hints */ if (!strncmp(value, "DONE:", 5)) { fire_timeout(c, false); @@ -346,7 +341,7 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli } } -static void call_hints_function(Client *c, const char *func, char* args) +static void call_hints_function(Client *c, const char *func, const char* args) { char *jscode; @@ -367,7 +362,6 @@ static void fire_timeout(Client *c, gboolean on) if (on) { millis = GET_INT(c, "hint-timeout"); - g_print("millis %d", millis); if (millis) { hints.timeout_id = g_timeout_add(millis, (GSourceFunc)fire_cb, c); } diff --git a/src/hints.h b/src/hints.h index 1b23e86e..6821554c 100644 --- a/src/hints.h +++ b/src/hints.h @@ -23,7 +23,7 @@ #include "main.h" VbResult hints_keypress(Client *c, int key); -void hints_create(Client *c, char *input); +void hints_create(Client *c, const char *input); void hints_fire(Client *c); void hints_follow_link(Client *c, gboolean back, int count); void hints_increment_uri(Client *c, int count); diff --git a/src/normal.c b/src/normal.c index 156ef52a..73c9a3a0 100644 --- a/src/normal.c +++ b/src/normal.c @@ -621,7 +621,7 @@ static VbResult normal_pass(Client *c, const NormalCmdInfo *info) static VbResult normal_prevnext(Client *c, const NormalCmdInfo *info) { -#if 0 /* TODO need hinting to be available */ +#if 0 /* TODO implement outside of hints.js */ int count = info->count ? info->count : 1; if (info->key2 == ']') { hints_follow_link(FALSE, count); diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 6f3530f2..c381e45a 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -397,18 +397,15 @@ var hints = Object.freeze((function(){ /* 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"); + if (newWin && e.hasAttribute('href')) { + /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's + * target to _blank. Therefore, we can't simulate ctrl-click through _blank like we + * used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a + * simple link. In other cases, we fire the even normally. + */ + window.open(e.getAttribute('href'), '_blank'); } - /* 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; + click(e); } /* set focus on hint with given index valid hints array */ @@ -424,24 +421,22 @@ var hints = Object.freeze((function(){ 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 click(e) { + mouseEvent(e, "mouseover"); + mouseEvent(e, "mousedown"); + mouseEvent(e, "mouseup"); + mouseEvent(e, "click"); } - function mouseEvent(e, name, ctrl) { + function mouseEvent(e, name) { 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 + false, false, false, false, 0, null ); e.dispatchEvent(evObj); } From 36cbf067c0d944f510e78b709b9b8d19f7699b9f Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 5 May 2017 10:33:35 -0400 Subject: [PATCH 136/201] Make the ";y" (yanking) hint command work --- src/command.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/command.c b/src/command.c index 204bf05d..88962477 100644 --- a/src/command.c +++ b/src/command.c @@ -130,7 +130,10 @@ gboolean command_yank(Client *c, const Arg *arg, char buf) g_assert(c); g_assert(arg); g_assert(c->webview); - g_assert(arg->i == COMMAND_YANK_URI || arg->i == COMMAND_YANK_SELECTION); + g_assert( + arg->i == COMMAND_YANK_URI || + arg->i == COMMAND_YANK_SELECTION || + arg->i == COMMAND_YANK_ARG); if (arg->i == COMMAND_YANK_URI) { if ((uri = webkit_web_view_get_uri(c->webview))) { @@ -141,6 +144,9 @@ gboolean command_yank(Client *c, const Arg *arg, char buf) webkit_web_view_execute_editing_command(c->webview, WEBKIT_EDITING_COMMAND_COPY); /* read back copy from clipboard */ yanked = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); + } else { + /* use current arg.s as new clipboard content */ + yanked = g_strdup(arg->s); } if(!yanked) { From 199113e143a0dc4b8a242fc51b8735e4c9ac0309 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 00:34:51 +0200 Subject: [PATCH 137/201] Allow to quit hint mode via #349. There are some pages where the hinting could not be started and the input box is filled with ';o'. If the user uses this key is also given to the hinting mode which can't handle this so the user has no way to get back to normal mode. Moved the case to the beginning of the key handler to make sure the user can abort the current mode by . --- src/ex.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ex.c b/src/ex.c index f558e4c3..fde1bb78 100644 --- a/src/ex.c +++ b/src/ex.c @@ -241,6 +241,11 @@ VbResult ex_keypress(Client *c, int key) VbResult res; const char *text; + if (key == CTRL('C')) { + vb_enter(c, 'n'); + return RESULT_COMPLETE; + } + /* delegate call to hint mode if this is active */ if (c->mode->flags & FLAG_HINTING && RESULT_COMPLETE == hints_keypress(c, key)) { @@ -286,7 +291,6 @@ VbResult ex_keypress(Client *c, int key) break; case CTRL('['): - case CTRL('C'): vb_enter(c, 'n'); vb_input_set_text(c, ""); break; From faba66545756a7c97c5a034f2bae63cc8ee82184 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 00:39:00 +0200 Subject: [PATCH 138/201] Inject hinting script only in top frame #349. We can not address sub frame scripts at the time and the hinting script is able to process also frames, when this is allowed according to the same origin policy. So there is no need to inject the script into multiple frames. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index cace512d..89988bf0 100644 --- a/src/main.c +++ b/src/main.c @@ -1682,7 +1682,7 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) /* Inject the global hints script. */ script = webkit_user_script_new(HINTS, - WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES, + WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL); webkit_user_content_manager_add_script(ucm, script); webkit_user_script_unref(script); From 4705fb296c8cb2e41f1a02d639dab8ab89aff43c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 00:50:38 +0200 Subject: [PATCH 139/201] Don't fail on same origin policy violations #349. --- src/scripts/hints.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index c381e45a..80c5df25 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -209,9 +209,11 @@ var hints = Object.freeze((function(){ /* recurse into any iframe or frame element */ for (i = 0; i < win.frames.length; i++) { - var rect, - f = win.frames[i], - e = f.frameElement; + try { + var rect, f = win.frames[i], e = f.frameElement; + } catch (ex) { + continue; + } if (isVisible(e)) { rect = e.getBoundingClientRect(); From cb3af64b6cc851537f32615f3a1f8cb734605fcf Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 01:30:16 +0200 Subject: [PATCH 140/201] Fixed missed quotes around filter value #349. --- src/hints.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/hints.c b/src/hints.c index 8a5f9212..b24b3134 100644 --- a/src/hints.c +++ b/src/hints.c @@ -72,8 +72,8 @@ VbResult hints_keypress(Client *c, int key) } else { fire_timeout(c, true); /* try to handle the key by the javascript */ - call_hints_function(c, "update", (char[]){key, '\0'}); - return RESULT_COMPLETE; + call_hints_function(c, "update", (char[]){'"', key, '"', '\0'}); + return RESULT_ERROR; } fire_timeout(c, false); @@ -142,10 +142,9 @@ void hints_create(Client *c, const char *input) return; } - call_hints_function( - c, "filter", - *(input + hints.promptlen) ? input + hints.promptlen : "" - ); + jsargs = g_strdup_printf("'%s'", *(input + hints.promptlen) ? input + hints.promptlen : ""); + call_hints_function(c, "filter", jsargs); + g_free(jsargs); } void hints_focus_next(Client *c, const gboolean back) @@ -348,7 +347,6 @@ static void call_hints_function(Client *c, const char *func, const char* args) jscode = g_strdup_printf("hints.%s(%s);", func, args); ext_proxy_eval_script(c, jscode, (GAsyncReadyCallback)hints_function_callback); g_free(jscode); - } static void fire_timeout(Client *c, gboolean on) From c55a758634321161b2b0c1c8e1b77b2595730cf7 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 02:03:19 +0200 Subject: [PATCH 141/201] Run hint script syncron #349. When we run the hinting asyn over dbus we can't use the return value of the hinting to check if the action was done or not. So it was not possible to undo last number filter on pressing backspace. --- src/ext-proxy.c | 30 ++++++++++++++++++++++++++++++ src/ext-proxy.h | 1 + src/hints.c | 43 +++++++++++++++++++------------------------ 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 6957f97f..4bee71c1 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -32,6 +32,8 @@ static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, gpointer data); static void dbus_call(Client *c, const char *method, GVariant *param, GAsyncReadyCallback callback); +static GVariant *dbus_call_sync(Client *c, const char *method, GVariant + *param); 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, @@ -162,6 +164,11 @@ void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) } } +GVariant *ext_proxy_eval_script_sync(Client *c, char *js) +{ + return dbus_call_sync(c, "EvalJs", g_variant_new("(s)", js)); +} + /** * Request the web extension to focus first editable element. * Returns whether an focusable element was found or not. @@ -193,6 +200,29 @@ static void dbus_call(Client *c, const char *method, GVariant *param, g_dbus_proxy_call(c->dbusproxy, method, param, G_DBUS_CALL_FLAGS_NONE, -1, NULL, callback, c); } +/** + * Call a dbus method syncron. + */ +static GVariant *dbus_call_sync(Client *c, const char *method, GVariant *param) +{ + GVariant *result = NULL; + GError *error = NULL; + + if (!c->dbusproxy) { + return NULL; + } + + result = g_dbus_proxy_call_sync(c->dbusproxy, method, param, + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + + if (error) { + g_warning("Failed dbus method %s: %s", method, error->message); + g_error_free(error); + } + + return result; +} + /** * Called when the web context created the page. * diff --git a/src/ext-proxy.h b/src/ext-proxy.h index 7c70c723..82e706e3 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -24,6 +24,7 @@ const char *ext_proxy_init(void); void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback); +GVariant *ext_proxy_eval_script_sync(Client *c, char *js); void ext_proxy_focus_input(Client *c); void ext_proxy_set_header(Client *c, const char *headers); diff --git a/src/hints.c b/src/hints.c index b24b3134..43a7834c 100644 --- a/src/hints.c +++ b/src/hints.c @@ -44,7 +44,7 @@ static struct { extern struct Vimb vb; -static void call_hints_function(Client *c, const char *func, const char* args); +static gboolean call_hints_function(Client *c, const char *func, const char* args); static void fire_timeout(Client *c, gboolean on); static gboolean fire_cb(gpointer data); @@ -55,10 +55,11 @@ VbResult hints_keypress(Client *c, int key) hints_fire(c); return RESULT_COMPLETE; - } else if (key == CTRL('H')) { + } else if (key == CTRL('H')) { /* backspace */ fire_timeout(c, false); - call_hints_function(c, "update", "null"); - return RESULT_MORE; // continue handling the backspace + if (call_hints_function(c, "update", "null")) { + return RESULT_COMPLETE; + } } else if (key == KEY_TAB) { fire_timeout(c, false); hints_focus_next(c, false); @@ -72,8 +73,9 @@ VbResult hints_keypress(Client *c, int key) } else { fire_timeout(c, true); /* try to handle the key by the javascript */ - call_hints_function(c, "update", (char[]){'"', key, '"', '\0'}); - return RESULT_ERROR; + if (call_hints_function(c, "update", (char[]){'"', key, '"', '\0'})) { + return RESULT_COMPLETE; + } } fire_timeout(c, false); @@ -247,23 +249,23 @@ gboolean hints_parse_prompt(const char *prompt, char *mode, gboolean *is_gmode) return res; } -static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Client *c) +static gboolean call_hints_function(Client *c, const char *func, const char* args) { + GVariant *return_value; + char *jscode, *value = NULL; gboolean success = FALSE; - char *value = NULL; - GVariant *return_value = g_dbus_proxy_call_finish(proxy, result, NULL); + jscode = g_strdup_printf("hints.%s(%s);", func, args); + return_value = ext_proxy_eval_script_sync(c, jscode); + g_free(jscode); + if (!return_value) { - return; + return FALSE; } g_variant_get(return_value, "(bs)", &success, &value); - if (!success) { - return; - } - - if (!strncmp(value, "ERROR:", 6)) { - return; + if (!success || !strncmp(value, "ERROR:", 6)) { + return FALSE; } /* following return values mark fired hints */ @@ -338,15 +340,8 @@ static void hints_function_callback(GDBusProxy *proxy, GAsyncResult *result, Cli #endif } } -} -static void call_hints_function(Client *c, const char *func, const char* args) -{ - char *jscode; - - jscode = g_strdup_printf("hints.%s(%s);", func, args); - ext_proxy_eval_script(c, jscode, (GAsyncReadyCallback)hints_function_callback); - g_free(jscode); + return TRUE; } static void fire_timeout(Client *c, gboolean on) From d33dfd31c148f9c82610e876f75fb16468d8d6c1 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 13:44:05 +0200 Subject: [PATCH 142/201] Inject global hints script during setting. In case the user disabled user scripts, also the hints script was removed from the web content manager. So if the page was reloaded the hinting did not work. To avoid the disabling of vimb internal used scripts, we set them always during set up of the settings. So they are readded in case all scripts are removed from the web content manager. --- src/main.c | 9 --------- src/setting.c | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main.c b/src/main.c index 89988bf0..22c6a8c9 100644 --- a/src/main.c +++ b/src/main.c @@ -40,7 +40,6 @@ #include "main.h" #include "map.h" #include "normal.h" -#include "scripts/scripts.h" #include "setting.h" #include "shortcut.h" #include "util.h" @@ -1652,7 +1651,6 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) { WebKitWebView *new; WebKitUserContentManager *ucm; - WebKitUserScript *script; /* create a new webview */ if (webview) { @@ -1680,13 +1678,6 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) g_signal_connect(webkit_web_context_get_default(), "download-started", G_CALLBACK(on_webctx_download_started), c); - /* Inject the global hints script. */ - script = webkit_user_script_new(HINTS, - WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, - WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL); - webkit_user_content_manager_add_script(ucm, script); - webkit_user_script_unref(script); - /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); diff --git a/src/setting.c b/src/setting.c index 92b0f42a..f9fcfb27 100644 --- a/src/setting.c +++ b/src/setting.c @@ -25,6 +25,7 @@ #include "ext-proxy.h" #include "main.h" #include "setting.h" +#include "scripts/scripts.h" #include "shortcut.h" typedef enum { @@ -619,6 +620,13 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, webkit_user_content_manager_remove_all_scripts(ucm); } + /* Inject the global hints script. */ + script = webkit_user_script_new(HINTS, + WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, + WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL); + webkit_user_content_manager_add_script(ucm, script); + webkit_user_script_unref(script); + return CMD_SUCCESS; } From 746a74af99ef35af21d75432f1a1050dae0a3c7c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 14:19:40 +0200 Subject: [PATCH 143/201] Prefix minified content by file type. Avoid naming collisions and prefix the minified scripts by JS_ so that it's obvious what's their content. --- src/Makefile | 6 +++--- src/normal.c | 2 +- src/scripts/js2h.sh | 2 +- src/setting.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index ae8d14bc..50e267d3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,9 +1,9 @@ BASEDIR=.. include $(BASEDIR)/config.mk -SUBDIRS = webextension -OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) -JSFILES = $(wildcard scripts/*.js) +SUBDIRS = webextension +OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) +JSFILES = $(wildcard scripts/*.js) all: vimb $(SUBDIRS:%=%.subdir-all) diff --git a/src/normal.c b/src/normal.c index 73c9a3a0..9ea22ef3 100644 --- a/src/normal.c +++ b/src/normal.c @@ -495,7 +495,7 @@ static VbResult normal_increment_decrement(Client *c, const NormalCmdInfo *info) char *js; int count = info->count ? info->count : 1; - js = g_strdup_printf(INCREMENT_URI_NUMBER, info->key == CTRL('A') ? count : -count); + js = g_strdup_printf(JS_INCREMENT_URI_NUMBER, info->key == CTRL('A') ? count : -count); ext_proxy_eval_script(c, js, NULL); g_free(js); diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index 4a76777f..4a50c12d 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -15,7 +15,7 @@ fi # Remove the path and .js file extension and turn all chars to upper case to # get the constant name. -CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:.js$::g' | tr a-z A-Z) +CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:^\(.*\).\(js\):\2_\1:g' | tr a-z A-Z) # minify the script cat $FILE | \ diff --git a/src/setting.c b/src/setting.c index f9fcfb27..0df18811 100644 --- a/src/setting.c +++ b/src/setting.c @@ -621,7 +621,7 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, } /* Inject the global hints script. */ - script = webkit_user_script_new(HINTS, + script = webkit_user_script_new(JS_HINTS, WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL); webkit_user_content_manager_add_script(ucm, script); From 3b9bf50b4cbcf5b4b78cec47fe2454ba6493aa59 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 14:21:27 +0200 Subject: [PATCH 144/201] Set hint css vie web content manager #349. Add the hint css direct to webkit instead of manipulating the DOM to add a style node. By the way the default style was removed from config.def.h into src/scripts directory so that the css is minified during compilation. --- src/Makefile | 7 ++++--- src/config.def.h | 32 -------------------------------- src/scripts/hints.css | 28 ++++++++++++++++++++++++++++ src/scripts/hints.js | 15 --------------- src/scripts/js2h.sh | 11 +++++------ src/setting.c | 8 ++++++++ 6 files changed, 45 insertions(+), 56 deletions(-) create mode 100644 src/scripts/hints.css diff --git a/src/Makefile b/src/Makefile index 50e267d3..f60d2ce2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,6 +4,7 @@ include $(BASEDIR)/config.mk SUBDIRS = webextension OBJ = $(patsubst %.c, %.o, $(wildcard *.c)) JSFILES = $(wildcard scripts/*.js) +CSSFILES = $(wildcard scripts/*.css) all: vimb $(SUBDIRS:%=%.subdir-all) @@ -22,10 +23,10 @@ config.h: @echo create $@ from config.def.h $(Q)cp config.def.h $@ -scripts/scripts.h: $(JSFILES) +scripts/scripts.h: $(JSFILES) $(CSSFILES) $(Q)$(RM) $@ - @echo "create $@ from *.js" - $(Q)for file in $(JSFILES); do \ + @echo "create $@ from *.{css,js}" + $(Q)for file in $(JSFILES) $(CSSFILES); do \ ./scripts/js2h.sh $$file >> $@; \ done diff --git a/src/config.def.h b/src/config.def.h index 0e821370..44f9351f 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -52,35 +52,3 @@ #define SETTING_HOME_PAGE "about:blank" #define MAXIMUM_HINTS 500 - -/* 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;\ -transition:all 0 !important;\ -transition-delay:all 0 !important\ -}\ -._hintElem._hintFocus{\ -background-color:#8f0 !important\ -}\ -._hintLabel._hintFocus{\ -z-index:100001;\ -opacity:1\ -}" - diff --git a/src/scripts/hints.css b/src/scripts/hints.css new file mode 100644 index 00000000..c0611213 --- /dev/null +++ b/src/scripts/hints.css @@ -0,0 +1,28 @@ +#_hintContainer{ + position:static +} +._hintLabel{ + -webkit-transform:translate(-4px,-4px); + background-color:#fff; + border:1px solid #444; + color:#000; + font:bold .8em monospace; + margin:0; + opacity:0.7; + padding:0px 1px; + position:absolute; + z-index:100000 +} +._hintElem{ + background-color:#ff0 !important; + color:#000 !important; + transition-delay:all 0 !important; + transition:all 0 !important +} +._hintElem._hintFocus{ + background-color:#8f0 !important +} +._hintLabel._hintFocus{ + opacity:1; + z-index:10000 +} diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 80c5df25..eeef5b7e 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -197,9 +197,6 @@ var hints = Object.freeze((function(){ if (doc.body) { doc.body.appendChild(hDiv); } - /* create the default style sheet */ - createStyle(doc); - docs.push({ doc: doc, start: start, @@ -318,18 +315,6 @@ var hints = Object.freeze((function(){ 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 */ diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index 4a50c12d..ce38d9d9 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -13,13 +13,13 @@ if [ ! -r "$FILE" ]; then exit 1 fi -# Remove the path and .js file extension and turn all chars to upper case to -# get the constant name. -CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:^\(.*\).\(js\):\2_\1:g' | tr a-z A-Z) +# Put file extension and _ before file name, turn all to upper case to get the +# constant name. +CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:^\(.*\).\(css\|js\):\2_\1:g' | tr a-z A-Z) # minify the script cat $FILE | \ -# removove single line comments +# remove single line comments sed -e 's|^//.*$||g' | \ # remove linebreaks tr '\n\r' ' ' | \ @@ -32,8 +32,7 @@ sed -e 's:/\*[^*]*\*/::g' \ # ecaspe sed -e 's|\\x20| |g' \ -e 's|\\|\\\\|g' \ - -e 's|"|\\"|g' \ - -e 's|HINT_CSS|" HINT_CSS "|' | \ + -e 's|"|\\"|g' | \ # write opener with the starting and ending quote char sed -e "1s/^/#define $CONSTANT \"/" \ -e '$s/$/"\n/' diff --git a/src/setting.c b/src/setting.c index 0df18811..54e9b183 100644 --- a/src/setting.c +++ b/src/setting.c @@ -657,6 +657,14 @@ static int user_style(Client *c, const char *name, DataType type, void *value, v webkit_user_content_manager_remove_all_style_sheets(ucm); } + /* Inject the global styles with author level to allow restyling by user + * style sheets. */ + style = webkit_user_style_sheet_new(CSS_HINTS, + WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, + WEBKIT_USER_STYLE_LEVEL_AUTHOR, NULL, NULL); + webkit_user_content_manager_add_style_sheet(ucm, style); + webkit_user_style_sheet_unref(style); + return CMD_SUCCESS; } From ceac41b78240ff602485c19081d7adbe6eb42be3 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 14:41:51 +0200 Subject: [PATCH 145/201] Workaround for hanging hints #349. --- src/ext-proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 4bee71c1..2cb2896d 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -213,7 +213,7 @@ static GVariant *dbus_call_sync(Client *c, const char *method, GVariant *param) } result = g_dbus_proxy_call_sync(c->dbusproxy, method, param, - G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + G_DBUS_CALL_FLAGS_NONE, 500, NULL, &error); if (error) { g_warning("Failed dbus method %s: %s", method, error->message); From d307f9ca34655de66303810bc64ee5021e3647d3 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 14:42:20 +0200 Subject: [PATCH 146/201] Clear hints on entering normal mode #349. --- src/normal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/normal.c b/src/normal.c index 9ea22ef3..4286cefc 100644 --- a/src/normal.c +++ b/src/normal.c @@ -23,6 +23,7 @@ #include "ascii.h" #include "command.h" #include "config.h" +#include "hints.h" #include "ext-proxy.h" #include "main.h" #include "normal.h" @@ -219,7 +220,7 @@ void normal_enter(Client *c) /* Make sure that when the browser area becomes visible, it will get mouse * and keyboard events */ gtk_widget_grab_focus(GTK_WIDGET(c->webview)); - /* TODO clear possible active hints */ + hints_clear(c); } /** From 6ace2f1d0d4ac45834e1625e6d9b3bf2195358d4 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 14:47:44 +0200 Subject: [PATCH 147/201] Use direct click method on element #349. Previously there was some hacky target="_blank" setting unsetting logic in the hints click method to force to open a hint into new instance or the same. But this does not work and the hack was previously removed. So there is no need to do the click event emulation by ourselves, it's already available as method on the elements itself. --- src/scripts/hints.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index eeef5b7e..1b69c06a 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -366,11 +366,11 @@ var hints = Object.freeze((function(){ if (tag === "input" || tag === "textarea" || tag === "select") { if (type === "radio" || type === "checkbox") { e.focus(); - click(e); + e.click(); return "DONE:"; } if (type === "submit" || type === "reset" || type === "button" || type === "image") { - click(e); + e.click(); return "DONE:"; } e.focus(); @@ -392,7 +392,7 @@ var hints = Object.freeze((function(){ */ window.open(e.getAttribute('href'), '_blank'); } - click(e); + e.click(); } /* set focus on hint with given index valid hints array */ @@ -411,13 +411,6 @@ var hints = Object.freeze((function(){ } } - function click(e) { - mouseEvent(e, "mouseover"); - mouseEvent(e, "mousedown"); - mouseEvent(e, "mouseup"); - mouseEvent(e, "click"); - } - function mouseEvent(e, name) { var evObj = e.ownerDocument.createEvent("MouseEvents"); evObj.initMouseEvent( From 6861e627ee63589cd8775daa85632dcfaf43c500 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 22:39:59 +0200 Subject: [PATCH 148/201] Fix wrong js to scroll to x percent of page. --- src/normal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/normal.c b/src/normal.c index 156ef52a..d96513d7 100644 --- a/src/normal.c +++ b/src/normal.c @@ -688,7 +688,7 @@ static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) case 'G': if (info->count) { js = g_strdup_printf( - "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));", + "window.scroll(window.scrollX, %d * ((document.documentElement.scrollHeight - window.innerHeight) / 100));", info->count); ext_proxy_eval_script(c, js, NULL); g_free(js); From fedeb3f319870b3b666ebae5bde0f164b2d775fe Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 7 May 2017 22:39:11 +0200 Subject: [PATCH 149/201] Show scroll indicator in status bar #354. --- src/ext-proxy.c | 24 +++++++++++- src/webextension/ext-main.c | 73 ++++++++++++++++++++++++++++++++----- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 6957f97f..99a26486 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -30,6 +30,10 @@ static gboolean on_new_connection(GDBusServer *server, GDBusConnection *connection, gpointer data); static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, gpointer data); +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, Client *c); static void dbus_call(Client *c, const char *method, GVariant *param, GAsyncReadyCallback callback); static void on_web_extension_page_created(GDBusConnection *connection, @@ -153,6 +157,20 @@ static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, 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, Client *c) +{ + g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent); + + vb_statusbar_update(c); +} + void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) { if (callback) { @@ -217,6 +235,10 @@ static void on_web_extension_page_created(GDBusConnection *connection, /* Set the dbus proxy on the right client based on page id. */ p->dbusproxy = (GDBusProxy*)data; - /* TODO Subscribe to debus signals here. */ + /* Subscribe to dbus signals here. */ + 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); } } diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index 46a8233a..c8dadd71 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -33,13 +33,12 @@ static void on_dbus_connection_created(GObject *source_object, GAsyncResult *result, gpointer data); static void add_onload_event_observers(WebKitDOMDocument *doc, WebKitWebExtension *extension); +static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, + gpointer data); static void emit_page_created(GDBusConnection *connection, guint64 pageid); static void emit_page_created_pending(GDBusConnection *connection); static void queue_page_created_signal(guint64 pageid); -#if 0 -static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, - GVariant *data); -#endif +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); @@ -72,6 +71,10 @@ static const char introspection_xml[] = " " " " " " + " " + " " + " " + " " " " " " " " @@ -208,10 +211,65 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, * the event observer where set up. */ /* TODO this is not needed for strict-focus=on */ on_editable_change_focus(target, NULL, extension); + + /* Observe scroll events to get current position in the document. */ + webkit_dom_event_target_add_event_listener(target, "scroll", + G_CALLBACK(on_document_scroll), false, NULL); + /* Call the callback explicitly to make sure we have the right position + * shown in statusbar also in cases the user does not scroll. */ + on_document_scroll(target, NULL, NULL); +} + +/** + * Callback called when the document is scrolled. + */ +static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, + gpointer data) +{ + WebKitDOMDocument *doc; + + 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); + } + + if (doc) { + WebKitDOMElement *b, *de; + glong max, scrollTop, scrollHeight, clientHeight; + guint percent = 0; + + de = webkit_dom_document_get_document_element(doc); + if (!de) { + return; + } + /* Get the clientHeight. */ + clientHeight = webkit_dom_element_get_client_height(WEBKIT_DOM_ELEMENT(de)); + + b = WEBKIT_DOM_ELEMENT(webkit_dom_document_get_body(doc)); + /* Get the scrollTop of the document or the body. */ + if (!(scrollTop = webkit_dom_element_get_scroll_top(de)) && b) { + scrollTop = webkit_dom_element_get_scroll_top(b); + } + /* Get the scrollHeight of the document or the body. */ + if (!(scrollHeight = webkit_dom_element_get_scroll_height(de)) && b) { + scrollHeight = webkit_dom_element_get_scroll_height(b); + } + + /* Get the maximum scrollable page size. This is the size of the whole + * document - height of the viewport. */ + max = scrollHeight - clientHeight ; + + if (scrollTop && max) { + percent = (guint)(0.5 + (scrollTop * 100 / max)); + } + dbus_emit_signal("VerticalScroll", g_variant_new("(tt)", max, percent)); + } } /** - * Emit the page created signal that is used in the ui process to finish the + * Emit the page created signal that is used in the UI process to finish the * dbus proxy connection. */ static void emit_page_created(GDBusConnection *connection, guint64 pageid) @@ -262,15 +320,13 @@ static void queue_page_created_signal(guint64 pageid) ext.page_created_signals = g_array_append_val(ext.page_created_signals, pageid); } -#if 0 /** * 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, WebKitWebExtension* extension, - GVariant *data) +static void dbus_emit_signal(const char *name, GVariant *data) { GError *error = NULL; @@ -287,7 +343,6 @@ static void dbus_emit_signal(const char *name, WebKitWebExtension* extension, g_error_free(error); } } -#endif /** * Handle dbus method calls. From 904f8267fce2037a87025d0dd06007daf5b4c208 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 8 May 2017 01:12:00 +0200 Subject: [PATCH 150/201] Moved scrolling logic into javascript. --- src/normal.c | 70 +------------------------------------------ src/scripts/scroll.js | 61 +++++++++++++++++++++++++++++++++++++ src/setting.c | 4 +-- 3 files changed, 64 insertions(+), 71 deletions(-) create mode 100644 src/scripts/scroll.js diff --git a/src/normal.c b/src/normal.c index 06db6437..b66948a9 100644 --- a/src/normal.c +++ b/src/normal.c @@ -651,77 +651,9 @@ static VbResult normal_quit(Client *c, const NormalCmdInfo *info) static VbResult normal_scroll(Client *c, const NormalCmdInfo *info) { - 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 commands. */ - if (VB_IS_CTRL(info->key)) { - gtk_widget_get_allocation(GTK_WIDGET(c->webview), &alloc); - page_height = (int)alloc.height; - } - - switch (info->key) { - case 'j': - y = count * c->config.scrollstep; - break; - case 'h': - x = -count * c->config.scrollstep; - break; - case 'k': - y = -count * c->config.scrollstep; - break; - case 'l': - x = count * c->config.scrollstep; - break; - case CTRL('D'): - y = count * page_height / 2; - break; - case CTRL('U'): - y = -count * page_height / 2; - break; - case CTRL('F'): - y = count * page_height; - break; - case CTRL('B'): - y = -count * page_height; - break; - case 'G': - if (info->count) { - js = g_strdup_printf( - "window.scroll(window.scrollX, %d * ((document.documentElement.scrollHeight - window.innerHeight) / 100));", - info->count); - ext_proxy_eval_script(c, js, NULL); - g_free(js); - return RESULT_COMPLETE; - } - - /* Without count scroll to the end of the page. */ - ext_proxy_eval_script(c, "window.scroll(window.scrollX, document.body.scrollHeight);", NULL); - return RESULT_COMPLETE; - case '0': - ext_proxy_eval_script(c, "window.scroll(0, window.scrollY);", NULL); - return RESULT_COMPLETE; - case '$': - ext_proxy_eval_script(c, "window.scroll(document.body.scrollWidth, window.scrollY);", NULL); - return RESULT_COMPLETE; - default: - if (info->key2 == 'g') { - if (info->count) { - js = g_strdup_printf( - "window.scroll(window.scrollX, %d * (1 + (document.height - window.innerHeight) / 100));", - info->count); - ext_proxy_eval_script(c, js, NULL); - g_free(js); - return RESULT_COMPLETE; - } - /* Without count gg scrolls to the top of the page. */ - ext_proxy_eval_script(c, "window.scroll(window.scrollX, 0);", NULL); - return RESULT_COMPLETE; - } - return RESULT_ERROR; - } - js = g_strdup_printf("window.scrollBy(%d,%d);", x, y); + js = g_strdup_printf("vbscroll('%c',%d,%d);", info->key, c->config.scrollstep, info->count); ext_proxy_eval_script(c, js, NULL); g_free(js); diff --git a/src/scripts/scroll.js b/src/scripts/scroll.js new file mode 100644 index 00000000..92fc4818 --- /dev/null +++ b/src/scripts/scroll.js @@ -0,0 +1,61 @@ +function vbscroll(mode, scrollStep, count) { + var w = window, + d = document, + x = y = 0, + ph = d.documentElement.clientHeight, + c = count||1, + rel = true; + switch (mode) { + case 'j': + y = c * scrollStep; + break; + case 'h': + x = -c * scrollStep; + break; + case 'k': + y = -c * scrollStep; + break; + case 'l': + x = c * scrollStep; + break; + case '\x04': /* ^D */ + y = c * ph / 2; + break; + case '\x15': /* ^U */ + y = -c * ph / 2; + break; + case '\x06': /* ^F */ + y = c * ph; + break; + case '\x02': /* ^B */ + y = -c * ph; + break; + case 'G': /* fall through - gg and G differ only in y value when no count is given */ + case 'g': + x = w.scrollX; + if (count) { + y = c * ((d.documentElement.scrollHeight - w.innerHeight) / 100); + } else { + y = 'G' == mode ? d.body.scrollHeight : 0; + } + rel = false; + break; + case '0': + y = w.scrollY; + rel = false; + break; + case '$': + x = d.body.scrollWidth; + y = w.scrollY; + rel = false; + break; + default: + return 1; + } + if (rel) { + w.scrollBy(x, y); + } else { + w.scroll(x, y); + } + return 0; +} diff --git a/src/setting.c b/src/setting.c index 54e9b183..296fe53b 100644 --- a/src/setting.c +++ b/src/setting.c @@ -620,8 +620,8 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, webkit_user_content_manager_remove_all_scripts(ucm); } - /* Inject the global hints script. */ - script = webkit_user_script_new(JS_HINTS, + /* Inject the global scripts. */ + script = webkit_user_script_new(JS_HINTS " " JS_SCROLL, WEBKIT_USER_CONTENT_INJECT_TOP_FRAME, WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END, NULL, NULL); webkit_user_content_manager_add_script(ucm, script); From 798e2e1080e208f89c6bf49ad202ba4490dcb03d Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Mon, 8 May 2017 20:10:35 -0400 Subject: [PATCH 151/201] hints: open links in async mode When firing hints, open them (either by simulating a click or through `window.open()`) in async mode with `window.setTimeout()`. If we don't do that, the `EvalJS` dbus call will itself timeout and we'll end up in an inconsistent state, that is, not back to Normal mode. ref #349 --- src/scripts/hints.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 1b69c06a..d70d3b27 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -384,15 +384,23 @@ var hints = Object.freeze((function(){ /* internal used methods */ function open(e, newWin) { + /* We call open() and click() in async mode to avoid return as fast as possible. + * If we don't return immediately, the EvalJS dbus call will probably timeout and cause + * errors. + */ if (newWin && e.hasAttribute('href')) { /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's * target to _blank. Therefore, we can't simulate ctrl-click through _blank like we * used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a * simple link. In other cases, we fire the even normally. */ - window.open(e.getAttribute('href'), '_blank'); + + window.setTimeout(function() { + window.open(e.getAttribute('href'), '_blank'); + }, 0 + ); } - e.click(); + window.setTimeout(function() {e.click();}, 0); } /* set focus on hint with given index valid hints array */ From dcc2c39757b707dbf9978f13a0b17dcb32be4ce9 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 9 May 2017 20:59:52 +0200 Subject: [PATCH 152/201] Don't give MFLAGS to make explicitly. The option given to the upper most make call are already given as MAKEFLAGS on recursive calls to make. So there is no need to give them explicitly. --- Makefile | 6 +++--- config.mk | 1 - src/Makefile | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 74bc6b81..e56e528d 100644 --- a/Makefile +++ b/Makefile @@ -32,15 +32,15 @@ uninstall: clean: $(SRCDIR).subdir-clean sandbox: - $(Q)$(MAKE) $(MFLAGS) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install + $(Q)$(MAKE) RUNPREFIX=$(CURDIR)/sandbox/usr PREFIX=/usr DESTDIR=./sandbox install runsandbox: sandbox sandbox/usr/bin/vimb %.subdir-all: - $(Q)$(MAKE) $(MFLAGS) -C $* + $(Q)$(MAKE) -C $* %.subdir-clean: - $(Q)$(MAKE) $(MFLAGS) -C $* clean + $(Q)$(MAKE) -C $* clean .PHONY: all options install uninstall clean sandbox runsandbox diff --git a/config.mk b/config.mk index d09d8e69..3a4964e0 100644 --- a/config.mk +++ b/config.mk @@ -40,4 +40,3 @@ EXTLDFLAGS = $(shell pkg-config --libs webkit2gtk-4.0) -shared CFLAGS += $(shell pkg-config --cflags $(LIBS)) CFLAGS += ${CPPFLAGS} LDFLAGS += $(shell pkg-config --libs $(LIBS)) -MFLAGS = --no-print-directory diff --git a/src/Makefile b/src/Makefile index f60d2ce2..7f037c63 100644 --- a/src/Makefile +++ b/src/Makefile @@ -35,9 +35,9 @@ scripts/scripts.h: $(JSFILES) $(CSSFILES) $(Q)$(CC) $(CFLAGS) -c -o $@ $< %.subdir-all: config.h - $(Q)$(MAKE) $(MFLAGS) -C $* + $(Q)$(MAKE) -C $* %.subdir-clean: - $(Q)$(MAKE) $(MFLAGS) -C $* clean + $(Q)$(MAKE) -C $* clean .PHONY: all clean From 6be041715f0934e93e985b6bc1063f1110d15f6b Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Wed, 10 May 2017 00:11:39 +0200 Subject: [PATCH 153/201] fixes hintElem opacity for non opaque elements Forces the hintElem to be fully opaque, even when attached to non opaque elements (e.g. {opacity:0.5}). #349 --- src/scripts/hints.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripts/hints.css b/src/scripts/hints.css index c0611213..f4fee78f 100644 --- a/src/scripts/hints.css +++ b/src/scripts/hints.css @@ -17,7 +17,8 @@ background-color:#ff0 !important; color:#000 !important; transition-delay:all 0 !important; - transition:all 0 !important + transition:all 0 !important; + opacity:1 !important } ._hintElem._hintFocus{ background-color:#8f0 !important From 6d284f0c4a0838096a90663973e5016a2839d5db Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 11 May 2017 14:02:42 +0200 Subject: [PATCH 154/201] Added hinting to man page. --- doc/vimb.1 | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index fa2f0431..9f68b16b 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -185,6 +185,104 @@ Scroll page \fIN\fP steps down. .TP .BI [ N ]k Scroll page \fIN\fP steps up. +.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 , or , , +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 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\fP +Selects the first highlighted element, or the current focused. +.IP "\fB\fP" +Moves the focus to the next hint element. +.IP "\fB\fP" +Moves the focus to the previous hint element. +.IP "\fB, 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 ;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 +.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 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 @@ -545,6 +643,13 @@ Contains the pid of the running Vimb instance. .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. +.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 .PD .PP Example: @@ -599,6 +704,9 @@ Curent opened URI. .TP .B \(dq/ Last search phrase. +.TP +.B \(dq; +Contains the last hinted URL. .PD .RE .TP @@ -705,6 +813,34 @@ If closed-max-items is set to 0, closed URLs will not be stored. .TP .B default-zoom (int) Default Full-Content zoom level in percent. Default is 100. +.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 .SH FILES .TP .IR $XDG_CONFIG_HOME/vimb[/PROFILE] From 67533d668cb8bde84d54c20f3e5de4c2e669de1f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 11 May 2017 14:35:45 +0200 Subject: [PATCH 155/201] Updated README. Also added known packages. --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ab7496d0..cd0b4643 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,56 @@ -# vimb - the vim like browser +# 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. +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. -If you like to have a working vimb, please use the master branch. +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 +- history for `ex` commands, search queries, URLs +- completions for: commands, URLs, bookmarked URLs, variable names of settings, search-queries +- hinting - marks links, form fields and other clickable elements to + be clicked, opened or inspected +- SSL validation against ca-certificate file +- user defined URL-shortcuts with placeholders +- read it later queue to collect URIs for later use +- multiple yank/paste registers + +## Packages + +- Gentoo [gentoo-git][], [gentoo][] + +## dependencies + +- webkit2gtk-4.0 >= 2.16.x + +## Install + +Edit `config.mk` to match your local setup. + +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 + make install + +## Mailing list + +- feature requests, issues and patches can be discussed on the [mailing list][mail] ## Patching and Coding style @@ -59,10 +105,6 @@ If you like to have a working vimb, please use the master branch. ├── scripts JavaScripts that are compiled in for 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 @@ -76,6 +118,11 @@ To run vimb without installation you could run as a sandbox like this This will compile and install vimb into the local _sandbox_ folder in the project directory. -# license +## license Information about the license are found in the file LICENSE. + +[gentoo-git]: https://github.com/tharvik/overlay/tree/master/www-client/vimb +[gentoo]: https://github.com/hsoft/portage-overlay/tree/master/www-client/vimb +[vimb]: https://fanglingsu.github.io/vimb/ "Vimb - Vim like browser project page" +[mail]: https://lists.sourceforge.net/lists/listinfo/vimb-users "vimb - mailing list" From 09b3e914fe40925dade82a2711af07e8c5f9c7f6 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 11 May 2017 14:38:59 +0200 Subject: [PATCH 156/201] Added inofficial arch linux pkgbuild. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cd0b4643..7ac0ff47 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ the project page of [Vimb][]. ## Packages +- Arch Linux [vimb-git][] - Gentoo [gentoo-git][], [gentoo][] ## dependencies @@ -122,6 +123,7 @@ project directory. Information about the license are found in the file LICENSE. +[arch-git]: https://github.com/fanglingsu/dotfiles/tree/master/build/vimb-git [gentoo-git]: https://github.com/tharvik/overlay/tree/master/www-client/vimb [gentoo]: https://github.com/hsoft/portage-overlay/tree/master/www-client/vimb [vimb]: https://fanglingsu.github.io/vimb/ "Vimb - Vim like browser project page" From 00a4a1283a4fc675f49caf2020b852b79abcf4ee Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 11 May 2017 14:50:01 +0200 Subject: [PATCH 157/201] Fixed possible null pointer dereference #392. --- src/ex.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ex.c b/src/ex.c index fde1bb78..3660ec75 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1324,6 +1324,10 @@ static gboolean history(Client *c, gboolean prev) exhist.active = new; } + if (!exhist.active) { + goto failed; + } + vb_echo_force(c, MSG_NORMAL, FALSE, "%s%s", exhist.prefix, (char*)exhist.active->data); g_free(input); From 5f091638effb44626488db8cb779e9cc9103fa49 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Thu, 11 May 2017 14:55:32 +0200 Subject: [PATCH 158/201] Removed dead code #393. --- src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.c b/src/main.c index 22c6a8c9..b6952c48 100644 --- a/src/main.c +++ b/src/main.c @@ -1158,7 +1158,6 @@ static gboolean on_webview_decide_policy(WebKitWebView *webview, return FALSE; 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); From c5412c9756d7ff09e0da728e2794cd0cc412383e Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 11 May 2017 23:15:45 +0200 Subject: [PATCH 159/201] implements external editor (closes #347) --- src/input.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/util.c | 38 ++++++++++++- src/util.h | 1 + 3 files changed, 186 insertions(+), 3 deletions(-) diff --git a/src/input.c b/src/input.c index 3e7943c1..f0ece141 100644 --- a/src/input.c +++ b/src/input.c @@ -28,6 +28,12 @@ #include "util.h" #include "ext-proxy.h" +typedef struct { + Client *c; + char *file; +} EditorData; + +static void resume_editor(GPid pid, int status, EditorData *data); /** * Function called when vimb enters the input mode. @@ -100,7 +106,147 @@ VbResult input_keypress(Client *c, int key) VbResult input_open_editor(Client *c) { - /* TODO should the editor be opened by the webextension or by the - * application? */ + char **argv, *file_path = NULL; + const char *text = NULL, *editor_command; + int argc; + GPid pid; + gboolean success; + GVariant *jsreturn; + + g_assert(c); + + /* get the editor command */ + editor_command = GET_CHAR(c, "editor-command"); + if (!editor_command || !*editor_command) { + vb_echo(c, MSG_ERROR, true, "No editor-command configured"); + return RESULT_ERROR; + } + + /* get the selected input element */ + jsreturn = ext_proxy_eval_script_sync(c, "vimb_input_mode_element.value"); + g_variant_get(jsreturn, "(bs)", &success, &text); + + if (!success || !text) { + return RESULT_ERROR; + } + + /* create a temp file to pass text to and from editor */ + 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 */ + ext_proxy_eval_script(c, "vimb_input_mode_element.disabled=true", NULL); + + /* watch the editor process */ + EditorData *data = g_slice_new0(EditorData); + data->file = file_path; + data->c = c; + g_child_watch_add(pid, (GChildWatchFunc)resume_editor, data); + return RESULT_COMPLETE; } + +static void resume_editor(GPid pid, int status, EditorData *data) +{ + char *text, *tmp; + char *jscode; + + g_assert(pid); + g_assert(data); + g_assert(data->c); + g_assert(data->file); + + if (status == 0) { + /* get the text the editor stored */ + text = util_get_file_contents(data->file, NULL); + + if (text) { + /* escape the text to form a valid JS string */ + /* TODO: could go into util.c maybe */ + struct search_replace { + const char* search; + const char* replace; + } escapes[] = { + {"\x01", ""}, + {"\x02", ""}, + {"\x03", ""}, + {"\x04", ""}, + {"\x05", ""}, + {"\x06", ""}, + {"\a", ""}, + {"\b", ""}, + {"\t", "\\t"}, + {"\n", "\\n"}, + {"\v", ""}, + {"\f", ""}, + {"\r", ""}, + {"\x0E", ""}, + {"\x0F", ""}, + {"\x10", ""}, + {"\x11", ""}, + {"\x12", ""}, + {"\x13", ""}, + {"\x14", ""}, + {"\x15", ""}, + {"\x16", ""}, + {"\x17", ""}, + {"\x18", ""}, + {"\x19", ""}, + {"\x1A", ""}, + {"\x1B", ""}, + {"\x1C", ""}, + {"\x1D", ""}, + {"\x1E", ""}, + {"\x1F", ""}, + {"\"", "\\\""}, + {"\x7F", ""}, + {NULL, NULL}, + }; + + for(struct search_replace *i = escapes; i->search; i++) { + tmp = util_str_replace(i->search, i->replace, text); + g_free(text); + text = tmp; + } + + /* put the text back into the element */ + jscode = g_strdup_printf("vimb_input_mode_element.value=\"%s\"", text); + ext_proxy_eval_script(data->c, jscode, NULL); + + g_free(jscode); + g_free(text); + } + } + + ext_proxy_eval_script(data->c, + "vimb_input_mode_element.disabled=false;" + "vimb_input_mode_element.focus()", NULL); + + g_unlink(data->file); + g_free(data->file); + g_slice_free(EditorData, data); + g_spawn_close_pid(pid); +} diff --git a/src/util.c b/src/util.c index f91dd892..54bc4163 100644 --- a/src/util.c +++ b/src/util.c @@ -110,6 +110,42 @@ gboolean util_create_dir_if_not_exists(const char *dirpath) return TRUE; } +/** + * 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 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; +} + /** * Expand ~user, ~/, $ENV and ${ENV} for given string into new allocated * string. @@ -717,7 +753,7 @@ char *util_strcasestr(const char *haystack, const char *needle) * 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) +char *util_str_replace(const char *search, const char *replace, const char *string) { if (!string) { return NULL; diff --git a/src/util.h b/src/util.h index 801654d1..9043eb23 100644 --- a/src/util.h +++ b/src/util.h @@ -33,6 +33,7 @@ typedef void *(*Util_Content_Func)(const char*, const char*); char *util_build_path(Client *c, const char *path, const char *dir); void util_cleanup(void); gboolean util_create_dir_if_not_exists(const char *dirpath); +gboolean util_create_tmp_file(const char *content, char **file); 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, ...); From 70b172403a3d97b25f039db658a0b38f495233a2 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 12 May 2017 00:08:45 +0200 Subject: [PATCH 160/201] Fixed wrong client to webpage relation #349. In case a related webview is created. The relation between webextensions web page and the client in the ui process where mismatched. In fact the relation was correct, but when a dbus call was fired from ui to webextension, the webextension used the last created web page to run javascript in or to focus input fields. This patch adds the page_id of the web page to the dbus calls that target a specific web page. So that th webextension can get the right page by this id to use to answer the call. Also the communication from webextension to ui lagged this essential information. So that a click to a editable filed in a related webview all related instances into input mode switched. --- src/ext-proxy.c | 51 +++++++++++------ src/main.c | 38 +++++++++++-- src/main.h | 1 + src/util.c | 4 ++ src/webextension/ext-main.c | 110 +++++++++++++++++++++--------------- 5 files changed, 140 insertions(+), 64 deletions(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 5dce15e6..4cdcaf74 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -28,12 +28,14 @@ static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, GIOStream *stream, GCredentials *credentials, gpointer data); static gboolean on_new_connection(GDBusServer *server, GDBusConnection *connection, gpointer data); +static void on_connection_close(GDBusConnection *connection, gboolean + remote_peer_vanished, GError *error, gpointer data); static void on_proxy_created (GDBusProxy *proxy, GAsyncResult *result, gpointer data); 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, Client *c); + GVariant *parameters, gpointer data); static void dbus_call(Client *c, const char *method, GVariant *param, GAsyncReadyCallback callback); static GVariant *dbus_call_sync(Client *c, const char *method, GVariant @@ -118,7 +120,7 @@ static gboolean on_new_connection(GDBusServer *server, /* Create dbus proxy. */ g_return_val_if_fail(G_IS_DBUS_CONNECTION(connection), FALSE); - /*g_signal_connect(connection, "closed", G_CALLBACK(connection_closed_cb), NULL);*/ + g_signal_connect(connection, "closed", G_CALLBACK(on_connection_close), NULL); g_dbus_proxy_new(connection, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, @@ -133,6 +135,14 @@ static gboolean on_new_connection(GDBusServer *server, return TRUE; } +static void on_connection_close(GDBusConnection *connection, gboolean + remote_peer_vanished, GError *error, gpointer data) +{ + if (error && !remote_peer_vanished) { + g_warning("Unexpected lost connection to web extension: %s", error->message); + } +} + static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, gpointer data) { @@ -166,9 +176,19 @@ static void on_proxy_created(GDBusProxy *new_proxy, GAsyncResult *result, 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, Client *c) + GVariant *parameters, gpointer data) { - g_variant_get(parameters, "(tt)", &c->state.scroll_max, &c->state.scroll_percent); + glong max; + guint percent; + guint64 pageid; + Client *c; + + g_variant_get(parameters, "(ttq)", &pageid, &max, &percent); + c = vb_get_client_for_page_id(pageid); + if (c) { + c->state.scroll_max = max; + c->state.scroll_percent = percent; + } vb_statusbar_update(c); } @@ -176,15 +196,15 @@ static void on_vertical_scroll(GDBusConnection *connection, void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) { if (callback) { - dbus_call(c, "EvalJs", g_variant_new("(s)", js), callback); + dbus_call(c, "EvalJs", g_variant_new("(ts)", js, c->page_id), callback); } else { - dbus_call(c, "EvalJsNoResult", g_variant_new("(s)", js), NULL); + dbus_call(c, "EvalJsNoResult", g_variant_new("(ts)", c->page_id, js), NULL); } } GVariant *ext_proxy_eval_script_sync(Client *c, char *js) { - return dbus_call_sync(c, "EvalJs", g_variant_new("(s)", js)); + return dbus_call_sync(c, "EvalJs", g_variant_new("(ts)", c->page_id, js)); } /** @@ -193,7 +213,7 @@ GVariant *ext_proxy_eval_script_sync(Client *c, char *js) */ void ext_proxy_focus_input(Client *c) { - dbus_call(c, "FocusInput", NULL, NULL); + dbus_call(c, "FocusInput", g_variant_new("(t)", c->page_id), NULL); } /** @@ -252,23 +272,22 @@ static void on_web_extension_page_created(GDBusConnection *connection, const char *interface_name, const char *signal_name, GVariant *parameters, gpointer data) { - Client *p; - guint64 page_id; + Client *c; + guint64 pageid; - g_variant_get(parameters, "(t)", &page_id); + g_variant_get(parameters, "(t)", &pageid); /* 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) { + c = vb_get_client_for_page_id(pageid); + if (c) { /* Set the dbus proxy on the right client based on page id. */ - p->dbusproxy = (GDBusProxy*)data; + c->dbusproxy = (GDBusProxy*)data; /* Subscribe to dbus signals here. */ 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); + (GDBusSignalCallback)on_vertical_scroll, NULL, NULL); } } diff --git a/src/main.c b/src/main.c index b6952c48..d394f12d 100644 --- a/src/main.c +++ b/src/main.c @@ -95,7 +95,7 @@ static void vimb_cleanup(void); static void vimb_setup(void); static WebKitWebView *webview_new(Client *c, WebKitWebView *webview); static void on_script_message_focus(WebKitUserContentManager *manager, - WebKitJavascriptResult *message, Client *c); + WebKitJavascriptResult *res, gpointer data); static gboolean profileOptionArgFunc(const gchar *option_name, const gchar *value, gpointer data, GError **error); @@ -278,6 +278,21 @@ void vb_enter_prompt(Client *c, char id, const char *prompt, gboolean print_prom } } +/** + * Returns the client for given page id. + */ +Client *vb_get_client_for_page_id(guint64 pageid) +{ + Client *c; + /* Search for the client with the same page id. */ + for (c = vb.clients; c && c->page_id != pageid; c = c->next); + + if (c) { + return c; + } + return NULL; +} + /** * Retrieves the content of the command line. * Returned string must be freed with g_free. @@ -1679,17 +1694,32 @@ static WebKitWebView *webview_new(Client *c, WebKitWebView *webview) /* Setup script message handlers. */ webkit_user_content_manager_register_script_message_handler(ucm, "focus"); - g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), c); + g_signal_connect(ucm, "script-message-received::focus", G_CALLBACK(on_script_message_focus), NULL); return new; } static void on_script_message_focus(WebKitUserContentManager *manager, - WebKitJavascriptResult *message, Client *c) + WebKitJavascriptResult *res, gpointer data) { + char *message; + GVariant *variant; + guint64 pageid; gboolean is_focused; + Client *c; + + message = util_js_result_as_string(res); + variant = g_variant_parse(G_VARIANT_TYPE("(tb)"), message, NULL, NULL, NULL); + g_free(message); + + g_variant_get(variant, "(tb)", &pageid, &is_focused); + g_variant_unref(variant); + + c = vb_get_client_for_page_id(pageid); + if (!c) { + return; + } - is_focused = (gboolean)util_js_result_as_number(message); /* Don't change the mode if we are in pass through mode. */ if (c->mode->id == 'n' && is_focused) { vb_enter(c, 'i'); diff --git a/src/main.h b/src/main.h index 38f88080..3b7a31ec 100644 --- a/src/main.h +++ b/src/main.h @@ -271,6 +271,7 @@ 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); +Client *vb_get_client_for_page_id(guint64 pageid); char *vb_input_get_text(Client *c); void vb_input_set_text(Client *c, const char *text); void vb_input_update_style(Client *c); diff --git a/src/util.c b/src/util.c index 54bc4163..38f5d98d 100644 --- a/src/util.c +++ b/src/util.c @@ -561,6 +561,10 @@ gboolean util_filename_fill_completion(Client *c, GtkListStore *store, const cha return found; } +/** + * Returns the script result as string. + * Returned string must be freed by g_free. + */ char *util_js_result_as_string(WebKitJavascriptResult *result) { JSValueRef value; diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index c8dadd71..d7ff217b 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -32,18 +32,20 @@ static gboolean on_authorize_authenticated_peer(GDBusAuthObserver *observer, static void on_dbus_connection_created(GObject *source_object, GAsyncResult *result, gpointer data); static void add_onload_event_observers(WebKitDOMDocument *doc, - WebKitWebExtension *extension); + WebKitWebPage *page); static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, - gpointer data); + WebKitWebPage *page); static void emit_page_created(GDBusConnection *connection, guint64 pageid); static void emit_page_created_pending(GDBusConnection *connection); static void queue_page_created_signal(guint64 pageid); static void dbus_emit_signal(const char *name, GVariant *data); +static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation, + WebKitWebExtension *extension, guint64 pageid); 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 void on_editable_change_focus(WebKitDOMEventTarget *target, - WebKitDOMEvent *event, WebKitWebExtension *extension); + WebKitDOMEvent *event, WebKitWebPage *page); static void on_page_created(WebKitWebExtension *ext, WebKitWebPage *webpage, gpointer data); static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extension); static gboolean on_web_page_send_request(WebKitWebPage *webpage, WebKitURIRequest *request, @@ -59,21 +61,25 @@ static const char introspection_xml[] = "" " " " " + " " " " " " " " " " " " + " " " " " " " " + " " " " " " " " " " " " + " " " " - " " + " " " " " " " " @@ -85,11 +91,8 @@ static const char introspection_xml[] = struct Ext { guint regid; GDBusConnection *connection; - WebKitWebPage *webpage; - WebKitDOMElement *active; GHashTable *headers; GHashTable *documents; - gboolean input_focus; GArray *page_created_signals; }; struct Ext ext = {0}; @@ -188,7 +191,7 @@ static void on_dbus_connection_created(GObject *source_object, * too. */ static void add_onload_event_observers(WebKitDOMDocument *doc, - WebKitWebExtension *extension) + WebKitWebPage *page) { WebKitDOMEventTarget *target; @@ -204,27 +207,27 @@ static void add_onload_event_observers(WebKitDOMDocument *doc, 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, extension); + G_CALLBACK(on_editable_change_focus), TRUE, page); webkit_dom_event_target_add_event_listener(target, "blur", - G_CALLBACK(on_editable_change_focus), TRUE, extension); + G_CALLBACK(on_editable_change_focus), TRUE, page); /* 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, extension); + on_editable_change_focus(target, NULL, page); /* Observe scroll events to get current position in the document. */ webkit_dom_event_target_add_event_listener(target, "scroll", - G_CALLBACK(on_document_scroll), false, NULL); + G_CALLBACK(on_document_scroll), false, page); /* Call the callback explicitly to make sure we have the right position * shown in statusbar also in cases the user does not scroll. */ - on_document_scroll(target, NULL, NULL); + on_document_scroll(target, NULL, page); } /** * Callback called when the document is scrolled. */ static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *event, - gpointer data) + WebKitWebPage *page) { WebKitDOMDocument *doc; @@ -264,7 +267,8 @@ static void on_document_scroll(WebKitDOMEventTarget *target, WebKitDOMEvent *eve if (scrollTop && max) { percent = (guint)(0.5 + (scrollTop * 100 / max)); } - dbus_emit_signal("VerticalScroll", g_variant_new("(tt)", max, percent)); + dbus_emit_signal("VerticalScroll", g_variant_new("(ttq)", + webkit_web_page_get_id(page), max, percent)); } } @@ -344,6 +348,19 @@ static void dbus_emit_signal(const char *name, GVariant *data) } } +static WebKitWebPage *get_web_page_or_return_dbus_error(GDBusMethodInvocation *invocation, + WebKitWebExtension *extension, guint64 pageid) +{ + WebKitWebPage *page = webkit_web_extension_get_page(extension, pageid); + if (!page) { + g_warning("invalid page id %lu", pageid); + g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, "Invalid page ID: %"G_GUINT64_FORMAT, pageid); + } + + return page; +} + /** * Handle dbus method calls. */ @@ -352,22 +369,29 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer extension) { char *value; + guint64 pageid; + WebKitWebPage *page; if (g_str_has_prefix(method, "EvalJs")) { char *result = NULL; gboolean success; - gboolean no_result = !g_strcmp0(method, "EvalJsNoResult"); - JSValueRef ref = NULL; + gboolean no_result; + JSValueRef ref = NULL; JSGlobalContextRef jsContext; - g_variant_get(parameters, "(s)", &value); + g_variant_get(parameters, "(ts)", &pageid, &value); + page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid); + if (!page) { + return; + } + no_result = !g_strcmp0(method, "EvalJsNoResult"); jsContext = webkit_frame_get_javascript_context_for_script_world( - webkit_web_page_get_main_frame(ext.webpage), - webkit_script_world_get_default() - ); + webkit_web_page_get_main_frame(page), + webkit_script_world_get_default() + ); - success = ext_util_js_eval(jsContext, value, &ref); + success = ext_util_js_eval(jsContext, value, &ref); if (no_result) { g_dbus_method_invocation_return_value(invocation, NULL); @@ -377,7 +401,12 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, g_free(result); } } else if (!g_strcmp0(method, "FocusInput")) { - ext_dom_focus_input(webkit_web_page_get_dom_document(ext.webpage)); + g_variant_get(parameters, "(t)", &pageid); + page = get_web_page_or_return_dbus_error(invocation, WEBKIT_WEB_EXTENSION(extension), pageid); + if (!page) { + return; + } + ext_dom_focus_input(webkit_web_page_get_dom_document(page)); g_dbus_method_invocation_return_value(invocation, NULL); } else if (!g_strcmp0(method, "SetHeaderSetting")) { g_variant_get(parameters, "(s)", &value); @@ -397,12 +426,13 @@ static void dbus_handle_method_call(GDBusConnection *conn, const char *sender, * WebKitDOMDOMWindow. */ static void on_editable_change_focus(WebKitDOMEventTarget *target, - WebKitDOMEvent *event, WebKitWebExtension *extension) + WebKitDOMEvent *event, WebKitWebPage *page) { - gboolean input_focus; WebKitDOMDocument *doc; WebKitDOMDOMWindow *dom_window; WebKitDOMElement *active; + GVariant *variant; + char *message; if (WEBKIT_DOM_IS_DOM_WINDOW(target)) { g_object_get(target, "document", &doc, NULL); @@ -417,9 +447,8 @@ static void on_editable_change_focus(WebKitDOMEventTarget *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) { + /* Don't do anything if there is no active element */ + if (!active) { return; } if (WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT(active)) { @@ -428,23 +457,19 @@ static void on_editable_change_focus(WebKitDOMEventTarget *target, iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT(active); subdoc = webkit_dom_html_iframe_element_get_content_document(iframe); - add_onload_event_observers(subdoc, extension); + add_onload_event_observers(subdoc, page); 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; - - webkit_dom_document_get_default_view(doc); - if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", input_focus ? "1" : "0")) { - g_warning("Error sending focus message"); - return; - } + variant = g_variant_new("(tb)", webkit_web_page_get_id(page), + ext_dom_is_editable(active)); + message = g_variant_print(variant, FALSE); + g_variant_unref(variant); + if (!webkit_dom_dom_window_webkit_message_handlers_post_message(dom_window, "focus", message)) { + g_warning("Error sending focus message"); } + g_free(message); g_object_unref(dom_window); } @@ -455,8 +480,6 @@ static void on_page_created(WebKitWebExtension *extension, WebKitWebPage *webpag { guint64 pageid = webkit_web_page_get_id(webpage); - /* Save the new created page in the extension data for later use. */ - ext.webpage = webpage; if (ext.connection) { emit_page_created(ext.connection, pageid); } else { @@ -481,8 +504,7 @@ static void on_web_page_document_loaded(WebKitWebPage *webpage, gpointer extensi } ext.documents = g_hash_table_new(g_direct_hash, g_direct_equal); - add_onload_event_observers(webkit_web_page_get_dom_document(webpage), - WEBKIT_WEB_EXTENSION(extension)); + add_onload_event_observers(webkit_web_page_get_dom_document(webpage), webpage); } /** From 0169d21fbf20c8d53a5226303fb1806b95038c16 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 12 May 2017 00:30:04 +0200 Subject: [PATCH 161/201] Fixed wrong link identifier. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ac0ff47..0776efdb 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ the project page of [Vimb][]. ## Packages -- Arch Linux [vimb-git][] +- Arch Linux [arch-git][] - Gentoo [gentoo-git][], [gentoo][] ## dependencies From 7b84be3d5a6925eef79d1351b78ac21de3a37322 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 12 May 2017 00:35:59 +0200 Subject: [PATCH 162/201] Added editor to man page #347. --- doc/vimb.1 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 9f68b16b..3b29ead9 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -250,6 +250,12 @@ Generate an `:open' prompt with hint's URI. .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 @@ -743,6 +749,9 @@ Switch back to normal mode. .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 WebKit. .TP @@ -814,6 +823,12 @@ If closed-max-items is set to 0, closed URLs will not be stored. .B default-zoom (int) Default Full-Content zoom level in percent. Default is 100. .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 vim %s'. +To use Gvim as the editor, it's necessary to call it with `-f' to run it +in the foreground. +.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. From e1e79b457607abd0cb2e253a948241c0c969d666 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 12 May 2017 13:16:19 +0200 Subject: [PATCH 163/201] Moved contribution related stuff from README to CONTRIBUTE. --- CONTRIBUTE.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 66 ----------------------------------------------- 2 files changed, 71 insertions(+), 66 deletions(-) create mode 100644 CONTRIBUTE.md diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md new file mode 100644 index 00000000..65987c8c --- /dev/null +++ b/CONTRIBUTE.md @@ -0,0 +1,71 @@ +# Contribute + +This document contains guidelines for contributing to vimb, as well as useful +hints when doing so. + +## Patching and Coding style + +### File Layout + +- Comment with LICENSE and possibly short explanation of file/tool +- Headers +- Macros +- Types +- Function declarations + - Include variable names + - For short files these can be left out + - Group/order in logical manner +- Global variables +- Function definitions in same order as declarations +- main + +### C Features + +- Do not mix declarations and code +- Do not use for loop initial declarations +- Use `/* */` for comments, not `//` + +### Headers + +- Place system/libc headers first in alphabetical order + - If headers must be included in a specific order comment to explain +- Place local headers after an empty line + +### Variables + +- Global variables not used outside translation unit should be declared static +- In declaration of pointers the `*` is adjacent to variable name, not type + +### Indentation + +- the code is indented by 4 spaces - if you use vim to code you can set + `:set expandtab ts=4 sts=4 sw=4` +- 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 for various purposes + └── webextension Source files for the webextension + +## 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 stored in. + +To run vimb without 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. + diff --git a/README.md b/README.md index 0776efdb..6d8124ff 100644 --- a/README.md +++ b/README.md @@ -53,72 +53,6 @@ root). - feature requests, issues and patches can be discussed on the [mailing list][mail] -## Patching and Coding style - -### File Layout - -- Comment with LICENSE and possibly short explanation of file/tool -- Headers -- Macros -- Types -- Function declarations - - Include variable names - - For short files these can be left out - - Group/order in logical manner -- Global variables -- Function definitions in same order as declarations -- main - -### C Features - -- Do not mix declarations and code -- Do not use for loop initial declarations -- Use `/* */` for comments, not `//` - -### Headers - -- Place system/libc headers first in alphabetical order - - If headers must be included in a specific order comment to explain -- Place local headers after an empty line - -### Variables - -- Global variables not used outside translation unit should be declared static -- In declaration of pointers the `*` is adjacent to variable name, not type - -### Indentation - -- the code is indented by 4 spaces - if you use vim to code you can set - `:set expandtab ts=4 sts=4 sw=4` -- 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 for various purposes - └── webextension Source files for the webextension - -## 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 stored in. - -To run vimb without 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. - ## license Information about the license are found in the file LICENSE. From c57e6d0eab493e68a3a94af1c06612e1ee1ca761 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 12 May 2017 13:26:39 +0200 Subject: [PATCH 164/201] Mention mailing list also for the contributors. --- CONTRIBUTE.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index 65987c8c..e88449e1 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -3,6 +3,13 @@ This document contains guidelines for contributing to vimb, as well as useful hints when doing so. +## Communication + +If you wan't to discuss some feature or provide some suggestion that can't be +done very well with the github issues. You should use the [mailing list][mail] +for this purpose. Else it's a good decision to use the features provided by +github for that. + ## Patching and Coding style ### File Layout @@ -69,3 +76,4 @@ To run vimb without installation you could run as a sandbox like this This will compile and install vimb into the local _sandbox_ folder in the project directory. +[mail]: https://lists.sourceforge.net/lists/listinfo/vimb-users "vimb - mailing list" From ad3bc486e63472f1472f6e049edf1bad472f3fb5 Mon Sep 17 00:00:00 2001 From: Michael Danilov Date: Thu, 27 Apr 2017 16:33:08 +0200 Subject: [PATCH 165/201] js2sh.sh: sed for was GNU-specific. --- config.mk | 1 + src/scripts/js2h.sh | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.mk b/config.mk index 3a4964e0..bf78a8b6 100644 --- a/config.mk +++ b/config.mk @@ -26,6 +26,7 @@ CFLAGS += -std=c99 -pipe -Wall CPPFLAGS += -DVERSION=\"${VERSION}\" -DEXTENSIONDIR=\"${EXTENSIONDIR}\" CPPFLAGS += -DPROJECT=\"vimb\" -DPROJECT_UCFIRST=\"Vimb\" CPPFLAGS += -D_XOPEN_SOURCE=500 +CPPFLAGS += -D__BSD_VISIBLE CPPFLAGS += -DGSEAL_ENABLE CPPFLAGS += -DGTK_DISABLE_SINGLE_INCLUDES CPPFLAGS += -DGDK_DISABLE_DEPRECATED diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index ce38d9d9..18aa586e 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -15,7 +15,7 @@ fi # Put file extension and _ before file name, turn all to upper case to get the # constant name. -CONSTANT=$(echo "$FILE" | sed 's:.*/::g' | sed 's:^\(.*\).\(css\|js\):\2_\1:g' | tr a-z A-Z) +CONSTANT=$(echo "$FILE" | sed -e 's:.*/::g' -e's/.*\.css$/CSS_&/g' -e's/.*\.js$/JS_&/g' -e's/\.css$//' -e's/\.js$//' | tr a-z A-Z) # minify the script cat $FILE | \ @@ -35,4 +35,5 @@ sed -e 's|\\x20| |g' \ -e 's|"|\\"|g' | \ # write opener with the starting and ending quote char sed -e "1s/^/#define $CONSTANT \"/" \ - -e '$s/$/"\n/' + -e '$s/$/"/' +echo "" From aedd02c495065995a9f1ed9f0194fc562333812c Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 12 May 2017 23:32:15 +0200 Subject: [PATCH 166/201] fixes :eval segfault (closes #397) --- src/ext-proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ext-proxy.c b/src/ext-proxy.c index 4cdcaf74..f1e42e5d 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -196,7 +196,7 @@ static void on_vertical_scroll(GDBusConnection *connection, void ext_proxy_eval_script(Client *c, char *js, GAsyncReadyCallback callback) { if (callback) { - dbus_call(c, "EvalJs", g_variant_new("(ts)", js, c->page_id), callback); + dbus_call(c, "EvalJs", g_variant_new("(ts)", c->page_id, js), callback); } else { dbus_call(c, "EvalJsNoResult", g_variant_new("(ts)", c->page_id, js), NULL); } From 92cac5208efe79135dbbb170669a5e5b63fead4e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 13 May 2017 01:13:58 +0200 Subject: [PATCH 167/201] Close comments in js on end of line. The multi line comments /* */ must be closed on the same line. Because the simple script that minifies the scripts a little can only remove those on same line closed comments properly. --- src/scripts/hints.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index d70d3b27..0c136440 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -291,7 +291,7 @@ var hints = Object.freeze((function(){ }; } - /* Retrun the hint string for a given number based on configured hintkeys */ + /* Return the hint string for a given number based on configured hintkeys */ function getHintString(n) { var res = [], len = config.hintKeys.length; @@ -384,17 +384,14 @@ var hints = Object.freeze((function(){ /* internal used methods */ function open(e, newWin) { - /* We call open() and click() in async mode to avoid return as fast as possible. - * If we don't return immediately, the EvalJS dbus call will probably timeout and cause - * errors. - */ + /* We call open() and click() in async mode to avoid return as fast as possible. */ + /* If we don't return immediately, the EvalJS dbus call will probably timeout and cause */ + /* errors. */ if (newWin && e.hasAttribute('href')) { - /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's - * target to _blank. Therefore, we can't simulate ctrl-click through _blank like we - * used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a - * simple link. In other cases, we fire the even normally. - */ - + /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's */ + /* target to _blank. Therefore, we can't simulate ctrl-click through _blank like we */ + /* used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a */ + /* simple link. In other cases, we fire the even normally. */ window.setTimeout(function() { window.open(e.getAttribute('href'), '_blank'); }, 0 From b6ecafa5533ff5147f459847db79bd62c4760d3c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 13 May 2017 01:29:49 +0200 Subject: [PATCH 168/201] Remove also whitespace around *%/[. --- src/scripts/js2h.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index 18aa586e..81425fc6 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -27,7 +27,7 @@ tr '\n\r' ' ' | \ sed -e 's:/\*[^*]*\*/::g' \ -e 's|[ ]\{2,\}| |g' \ -e 's|^ ||g' \ - -e "s|[ ]\{0,\}\([-!?<>:=(){};+\&\"',\|]\)[ ]\{0,\}|\1|g" \ + -e "s|[ ]\{0,\}\([-*[%/!?<>:=(){};+\&\"',\|]\)[ ]\{0,\}|\1|g" \ -e 's|"+"||g' | \ # ecaspe sed -e 's|\\x20| |g' \ From fc41555aaf0b9269c690bfde5f3e4f5928ad1ebe Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Mon, 15 May 2017 22:27:28 +0200 Subject: [PATCH 169/201] Add space after sed -e option #388. --- src/scripts/js2h.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/js2h.sh b/src/scripts/js2h.sh index 81425fc6..0efeaea9 100755 --- a/src/scripts/js2h.sh +++ b/src/scripts/js2h.sh @@ -15,7 +15,7 @@ fi # Put file extension and _ before file name, turn all to upper case to get the # constant name. -CONSTANT=$(echo "$FILE" | sed -e 's:.*/::g' -e's/.*\.css$/CSS_&/g' -e's/.*\.js$/JS_&/g' -e's/\.css$//' -e's/\.js$//' | tr a-z A-Z) +CONSTANT=$(echo "$FILE" | sed -e 's:.*/::g' -e 's/.*\.css$/CSS_&/g' -e 's/.*\.js$/JS_&/g' -e 's/\.css$//' -e 's/\.js$//' | tr a-z A-Z) # minify the script cat $FILE | \ From eb23cea534276f74926571393267827b2a74e7d0 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 16 May 2017 23:21:48 +0200 Subject: [PATCH 170/201] Added missed x-hint-command setting. --- doc/vimb.1 | 14 ++++++++++++++ src/setting.c | 1 + 2 files changed, 15 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 3b29ead9..ae3e3e07 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -856,6 +856,20 @@ 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 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=:sh! curl -e % ;" +This fills the inputbox with the prefilled download command and replaces +`%' with the current URI and `;' with the URI of the hinted element. +.PD +.RE .SH FILES .TP .IR $XDG_CONFIG_HOME/vimb[/PROFILE] diff --git a/src/setting.c b/src/setting.c index 296fe53b..09c40772 100644 --- a/src/setting.c +++ b/src/setting.c @@ -150,6 +150,7 @@ void setting_init(Client *c) i = 10; /* TODO should be global and not overwritten by a new client */ setting_add(c, "closed-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.closed_max); + setting_add(c, "x-hint-command", TYPE_CHAR, &":o ;", NULL, 0, NULL); #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT /* gui style settings vimb2 compatibility */ From cd16fd349d15637d4154a0b4a06ddb01273d4719 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 16 May 2017 23:23:33 +0200 Subject: [PATCH 171/201] Added current available settings to man page #365. --- doc/vimb.1 | 252 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 237 insertions(+), 15 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index ae3e3e07..6f9fd4a6 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -649,18 +649,6 @@ Contains the pid of the running Vimb instance. .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. -.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 -.PD -.PP -Example: -.EX -:sh ls -l $HOME .EE .RE .TP @@ -816,18 +804,90 @@ comparison for further typed chars. .SH SETTINGS All settings listed below can be set with the `:set' command. .TP +.B caret (bool) +Whether to enable accessibility enhanced keyboard navigation. +.TP +.B cookie-accept (string) +Cookie accept policy {`always', `never', `origin' (accept all non-third-party +cookies)}. +.TP .B closed-max-items (int) Maximum number of stored last closed URLs. If closed-max-items is set to 0, closed URLs will not be stored. .TP +.B completion-css (string) +CSS style applied to the inputbox completion list items. +.TP +.B completion-hover-css (string) +CSS style applied to the inputbox completion list item that is currently +hovered by the mouse. +.TP +.B completion-selected-css (string) +CSS style applied to the inputbox completion list item that is currently +selected. +.TP +.B cursivfont (string) +The font family used as the default for content using cursive font. +.TP +.B default-charset (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 default-zoom (int) Default Full-Content zoom level in percent. Default is 100. .TP +.B dns-prefetching (bool) +Indicates if Vimb prefetches domain names. +.TP +.B download-path (string) +Path to the default download directory. +If no download directory is 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 editor-command (string) -Command with placeholder '%s' called if form filler is opened with $EDITOR to +Command with placeholder '%s' called if form field is opened with $EDITOR to spawn the editor-like `x-terminal-emulator -e vim %s'. -To use Gvim as the editor, it's necessary to call it with `-f' to run it -in the foreground. +To use Gvim as the editor, it's necessary to call it with `-f' to run it in +the foreground. +.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 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. @@ -857,6 +917,163 @@ To have upper case hint labels, it's possible to add following css to the .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 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 . +.TP +.B images (bool) +Determines whether images should be automatically loaded or not. +.TP +.B input-autohide (bool) +If enabled the inputbox will be hidden whenever it contains no text. +.TP +.B input-css (string) +CSS style applied to the inputbox in normal state. +.TP +.B input-error-css (string) +CSS style applied to the inputbox in case of displayed error. +.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 print-backgrounds (bool) +Whether background images should be drawn during printing. +.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 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 scrollstep (int) +Number of pixel vimb scrolls if 'j' or 'k' is used. +.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 statusbar (bool) +Indicates if the statusbar should be shown. +.TP +.B status-css (string) +CSS style applied to the status bar on none https pages. +.TP +.B status-ssl-css (string) +CSS style applied to the status bar on https pages with trusted certificate. +.TP +.B status-sslinvalid-css (string) +CSS style applied to the status bar on https pages with untrusted certificate. +.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 tabs-to-links (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 timeoutlen (int) +The time in milliseconds that is waited for a key code or mapped key sequence +to complete. +.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 x-hint-command (string) Command used if hint mode ;x is fired. The command can be any vimb command string. @@ -870,6 +1087,11 @@ This fills the inputbox with the prefilled download command and replaces `%' with the current URI and `;' with the URI of the hinted element. .PD .RE +.TP +.B xssauditor (bool) +Whether to enable the XSS auditor. +This feature filters some kinds of reflective XSS attacks on vulnerable web +sites. .SH FILES .TP .IR $XDG_CONFIG_HOME/vimb[/PROFILE] From 8cfd4548f5b58cf7cc50ae5f857165c0890df89f Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 16 May 2017 23:39:21 +0200 Subject: [PATCH 172/201] Use dashed setting names. The mixes use of none separated settings names and those containing multiple dashes looked ugly. So use consequently dashes setting names. --- doc/vimb.1 | 37 ++++++++++++++++++++----------------- src/hints.c | 2 +- src/main.c | 2 +- src/setting.c | 29 ++++++++++++++--------------- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 6f9fd4a6..28c7e5df 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -189,7 +189,7 @@ Scroll page \fIN\fP steps up. 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'. +be marked by labels generated from configured `hint-keys'. Hints can be selected by using , or , , 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) @@ -826,14 +826,14 @@ hovered by the mouse. CSS style applied to the inputbox completion list item that is currently selected. .TP -.B cursivfont (string) +.B cursiv-font (string) The font family used as the default for content using cursive font. .TP .B default-charset (string) The default text charset used when interpreting content with an unspecified charset. .TP -.B defaultfont (string) +.B default-font (string) The font family to use as the default for content that does not specify a font. .TP @@ -856,7 +856,7 @@ spawn the editor-like `x-terminal-emulator -e vim %s'. To use Gvim as the editor, it's necessary to call it with `-f' to run it in the foreground. .TP -.B fontsize (int) +.B font-size (int) The default font size used to display text. .TP .B frame-flattening (bool) @@ -901,14 +901,14 @@ ambiguous. Timeout before automatically following a non-unique numerical hint. To disable auto fire of hints, set this value to 0. .TP -.B hintkeys (string) +.B hint-keys (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. +Note that the hint matching by label built of hint-keys is case sensitive. In this vimb differs from some other browsers that show hint labels in upper case, but match them lowercase. .RS @@ -976,16 +976,16 @@ 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) +.B minimum-font-size (int) The minimum font size used to display text. .TP -.B monofont (string) +.B monospace-font (string) The font family used as the default for content using monospace font. .TP -.B monofontsize (int) +.B monospace-font-size (int) Default font size for the monospace font. .TP -.B offlinecache (bool) +.B offline-cache (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. @@ -1002,16 +1002,16 @@ not allow a page to store data in the windows sessionStorage. .B plugins (bool) Determines whether or not plugins on the page are enabled. .TP -.B sansfont (string) +.B sans-serif-font (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 scrollstep (int) +.B scroll-step (int) Number of pixel vimb scrolls if 'j' or 'k' is used. .TP -.B seriffont (string) +.B serif-font (string) The font family used as the default for content using serif font. .TP .B site-specific-quirks (bool) @@ -1029,8 +1029,8 @@ 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 statusbar (bool) -Indicates if the statusbar should be shown. +.B status-bar (bool) +Indicates if the status bar should be shown. .TP .B status-css (string) CSS style applied to the status bar on none https pages. @@ -1038,7 +1038,7 @@ CSS style applied to the status bar on none https pages. .B status-ssl-css (string) CSS style applied to the status bar on https pages with trusted certificate. .TP -.B status-sslinvalid-css (string) +.B status-ssl-invalid-css (string) CSS style applied to the status bar on https pages with untrusted certificate. .TP .B strict-ssl (bool) @@ -1062,6 +1062,9 @@ to complete. .B useragent (string) The user-agent string used by WebKit. .TP +.B user-scripts (bool) +Indicates if user style sheet file $XDG_CONFIG_HOME/vimb/style.css is sourced. +.TP .B webaudio (bool) Enable or disable support for WebAudio on pages. WebAudio is an experimental proposal for allowing web pages @@ -1088,7 +1091,7 @@ This fills the inputbox with the prefilled download command and replaces .PD .RE .TP -.B xssauditor (bool) +.B xss-auditor (bool) Whether to enable the XSS auditor. This feature filters some kinds of reflective XSS attacks on vulnerable web sites. diff --git a/src/hints.c b/src/hints.c index 43a7834c..d5f5f40a 100644 --- a/src/hints.c +++ b/src/hints.c @@ -131,7 +131,7 @@ void hints_create(Client *c, const char *input) (char[]){hints.mode, '\0'}, hints.gmode ? "true" : "false", MAXIMUM_HINTS, - GET_CHAR(c, "hintkeys"), + GET_CHAR(c, "hint-keys"), GET_BOOL(c, "hint-follow-last") ? "true" : "false", GET_BOOL(c, "hint-number-same-length") ? "true" : "false" ); diff --git a/src/main.c b/src/main.c index d394f12d..e1116fb6 100644 --- a/src/main.c +++ b/src/main.c @@ -1584,7 +1584,7 @@ void vb_gui_style_update(Client *c, const char *setting_name_new, const char *se {"input-error-css", " #input.error{%s}"}, {"status-css", " #statusbar{%s}"}, {"status-ssl-css", " #statusbar.secure{%s}"}, - {"status-sslinvalid-css", " #statusbar.unsecure{%s}"}, + {"status-ssl-invalid-css", " #statusbar.unsecure{%s}"}, #endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ {0, 0}, diff --git a/src/setting.c b/src/setting.c index 09c40772..ce25d08b 100644 --- a/src/setting.c +++ b/src/setting.c @@ -78,19 +78,18 @@ void setting_init(Client *c) setting_add(c, "useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/603.1 (KHTML, like Gecko) " PROJECT "/" VERSION " Version/10.0 Safari/603.1", 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, "cursiv-font", 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, "default-font", 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(c, "fontsize", TYPE_INTEGER, &i, webkit, 0, "default-font-size"); + setting_add(c, "font-size", 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"); i = 1000; setting_add(c, "hint-timeout", TYPE_INTEGER, &i, NULL, 0, NULL); - setting_add(c, "hintkeys", TYPE_CHAR, &"0123456789", NULL, 0, NULL); + setting_add(c, "hint-keys", TYPE_CHAR, &"0123456789", NULL, 0, NULL); setting_add(c, "hint-follow-last", TYPE_BOOLEAN, &on, NULL, 0, NULL); setting_add(c, "hint-number-same-length", TYPE_BOOLEAN, &off, NULL, 0, NULL); setting_add(c, "html5-database", TYPE_BOOLEAN, &on, webkit, 0, "enable-html5-database"); @@ -106,15 +105,15 @@ void setting_init(Client *c) setting_add(c, "mediasource", TYPE_BOOLEAN, &off, webkit, 0, "enable-mediasource"); #endif i = 5; - setting_add(c, "minimumfontsize", TYPE_INTEGER, &i, webkit, 0, "minimum-font-size"); - setting_add(c, "monofont", TYPE_CHAR, &"monospace", webkit, 0, "monospace-font-family"); + setting_add(c, "minimum-font-size", TYPE_INTEGER, &i, webkit, 0, "minimum-font-size"); + setting_add(c, "monospace-font", TYPE_CHAR, &"monospace", webkit, 0, "monospace-font-family"); i = SETTING_DEFAULT_MONOSPACE_FONT_SIZE; - 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, "monospace-font-size", TYPE_INTEGER, &i, webkit, 0, "default-monospace-font-size"); + setting_add(c, "offline-cache", 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, "sans-serif-font", 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"); @@ -124,21 +123,21 @@ void setting_init(Client *c) 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"); + setting_add(c, "xss-auditor", TYPE_BOOLEAN, &on, webkit, 0, "enable-xss-auditor"); /* internal variables */ 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, "user-scripts", TYPE_BOOLEAN, &on, user_scripts, 0, NULL); setting_add(c, "cookie-accept", TYPE_CHAR, &"always", cookie_accept, 0, NULL); i = 40; - setting_add(c, "scrollstep", TYPE_INTEGER, &i, internal, 0, &c->config.scrollstep); + setting_add(c, "scroll-step", TYPE_INTEGER, &i, internal, 0, &c->config.scrollstep); setting_add(c, "home-page", TYPE_CHAR, &SETTING_HOME_PAGE, NULL, 0, NULL); i = 2000; /* 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); + setting_add(c, "status-bar", 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); @@ -183,7 +182,7 @@ void setting_init(Client *c) setting_add(c, "input-error-css", TYPE_CHAR, &"background-color:#f77;font:" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); setting_add(c, "status-css", TYPE_CHAR, &"color:#fff;background-color:#000;font:" SETTING_GUI_FONT_EMPH, gui_style, 0, NULL); setting_add(c, "status-ssl-css", TYPE_CHAR, &"background-color:#95e454;color:#000;", gui_style, 0, NULL); - setting_add(c, "status-sslinvalid-css", TYPE_CHAR, &"background-color:#f77;color:#000;", gui_style, 0, NULL); + setting_add(c, "status-ssl-invalid-css", TYPE_CHAR, &"background-color:#f77;color:#000;", gui_style, 0, NULL); #endif /* FEATURE_GUI_STYLE_VIMB2_COMPAT */ /* initialize the shortcuts and set the default shortcuts */ From 760e81dbcfd925ab3e2c3c3d6c5edc81dd6ac3ec Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 17 May 2017 00:01:12 +0200 Subject: [PATCH 173/201] Added missed ;x hinting to man page. --- doc/vimb.1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 28c7e5df..3c199e85 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -272,6 +272,10 @@ 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. From 9d49420d0728ab966dd289e7c91ea5e908147d65 Mon Sep 17 00:00:00 2001 From: Ferenc- Date: Thu, 18 May 2017 10:58:55 +0200 Subject: [PATCH 174/201] Eliminate dead code from main.c 1. Value stored in 'num' in line 161 was never read since it was unconditionally overwritten immediately in 166. 2. Value stored in 'suffix' in line 145 was never read since it was overwritten in both branches of 'if(dot_post)'. 3. Value stored in 'req' in line 1157 was never read if the branch of the first if statement is taken. Storing has been placed int to the second if statement, where it is actually used. --- src/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index e1116fb6..fd77f004 100644 --- a/src/main.c +++ b/src/main.c @@ -142,7 +142,6 @@ gboolean vb_download_set_destination(Client *c, WebKitDownload *download, GString *tmp; gssize suffix; int i = 1; - suffix = 2; /* position on .tar. (special case, extension with two dots), * position on last dot (if any) otherwise */ @@ -158,7 +157,6 @@ gboolean vb_download_set_destination(Client *c, WebKitDownload *download, } tmp = g_string_new(NULL); - num = g_strdup_printf("%d", i++); /* Construct a new complete download filepath with suffix before the * file extension. */ @@ -1154,7 +1152,6 @@ static gboolean on_webview_decide_policy(WebKitWebView *webview, 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); /* Ignore opening new window if this was started without user gesture. */ if (!webkit_navigation_action_is_user_gesture(a)) { @@ -1167,6 +1164,7 @@ static gboolean on_webview_decide_policy(WebKitWebView *webview, /* 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. */ + req = webkit_navigation_action_get_request(a); spawn_new_instance(webkit_uri_request_get_uri(req), TRUE); return TRUE; } From eb4b3574693104e3f77c6b4247166a6e0d4a67fc Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Thu, 18 May 2017 13:24:24 +0200 Subject: [PATCH 175/201] fixes serif-font setting name to match manpage --- src/setting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index ce25d08b..44edaddd 100644 --- a/src/setting.c +++ b/src/setting.c @@ -115,7 +115,7 @@ void setting_init(Client *c) setting_add(c, "private-browsing", TYPE_BOOLEAN, &off, webkit, 0, "enable-private-browsing"); setting_add(c, "sans-serif-font", 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, "serif-font", TYPE_CHAR, &"serif", webkit, 0, "serif-font-family"); setting_add(c, "site-specific-quirks", TYPE_BOOLEAN, &off, webkit, 0, "enable-site-specific-quirks"); setting_add(c, "smooth-scrolling", TYPE_BOOLEAN, &off, webkit, 0, "enable-smooth-scrolling"); setting_add(c, "spacial-navigation", TYPE_BOOLEAN, &off, webkit, 0, "enable-spatial-navigation"); From 6a49f89ed2f5499f43f0a63f91d19d8a77cbc961 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 19 May 2017 00:12:45 +0200 Subject: [PATCH 176/201] Increase default z-index for hint elements. There are some sites where the hint label where covered by on page elements using a high z-index. So increase our default too. --- src/scripts/hints.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/hints.css b/src/scripts/hints.css index f4fee78f..acbe28f5 100644 --- a/src/scripts/hints.css +++ b/src/scripts/hints.css @@ -11,7 +11,7 @@ opacity:0.7; padding:0px 1px; position:absolute; - z-index:100000 + z-index:225000 } ._hintElem{ background-color:#ff0 !important; @@ -25,5 +25,5 @@ } ._hintLabel._hintFocus{ opacity:1; - z-index:10000 + z-index:225000 } From 2562e602c999fd683b03e882f5ad8aa33935242e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 19 May 2017 01:38:58 +0200 Subject: [PATCH 177/201] Add settings for spell checking. --- doc/vimb.1 | 12 ++++++++++++ src/setting.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 3c199e85..3dd694f0 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -1033,6 +1033,18 @@ 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) +Enable or disable the spell checking feature. +.TP +.B spell-checking-languages (string) +Set comma separated list of spell checking languages to be used for spell +checking. +.br +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. +.TP .B status-bar (bool) Indicates if the status bar should be shown. .TP diff --git a/src/setting.c b/src/setting.c index 44edaddd..de33832a 100644 --- a/src/setting.c +++ b/src/setting.c @@ -61,6 +61,8 @@ static int user_style(Client *c, const char *name, DataType type, void *value, v 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); +static int webkit_spell_checking(Client *c, const char *name, DataType type, void *value, void *data); +static int webkit_spell_checking_language(Client *c, const char *name, DataType type, void *value, void *data); extern struct Vimb vb; @@ -150,6 +152,9 @@ void setting_init(Client *c) /* TODO should be global and not overwritten by a new client */ setting_add(c, "closed-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.closed_max); setting_add(c, "x-hint-command", TYPE_CHAR, &":o ;", NULL, 0, NULL); + setting_add(c, "spell-checking", TYPE_BOOLEAN, &off, webkit_spell_checking, 0, NULL); + setting_add(c, "spell-checking-languages", TYPE_CHAR, &"en_US", webkit_spell_checking_language, FLAG_LIST|FLAG_NODUP, NULL); + #ifdef FEATURE_GUI_STYLE_VIMB2_COMPAT /* gui style settings vimb2 compatibility */ @@ -713,3 +718,26 @@ static int webkit(Client *c, const char *name, DataType type, void *value, void } return CMD_SUCCESS; } + +static int webkit_spell_checking(Client *c, const char *name, DataType type, void *value, void *data) +{ + gboolean enabled = *((gboolean*)value); + + webkit_web_context_set_spell_checking_enabled( + webkit_web_context_get_default(), + enabled); + + return CMD_SUCCESS; +} + +static int webkit_spell_checking_language(Client *c, const char *name, DataType type, void *value, void *data) +{ + char **languages = g_strsplit((char*)value, ",", -1); + + webkit_web_context_set_spell_checking_languages( + webkit_web_context_get_default(), + (const char * const *)languages); + g_strfreev(languages); + + return CMD_SUCCESS; +} From fb122331ce31b79c37e6f1b4bec54e799fb4ddb2 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 19 May 2017 01:41:58 +0200 Subject: [PATCH 178/201] Update change settings in CHANGELOG. --- CHANGELOG.md | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43479982..ac3a7b91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,38 @@ ### Changed +* completely rebuild of vimb on webkit2 api. * Syntax for the font related gui settings has be changed. Fonts have to be given as `[ font-style | font-variant | font-weight | font-stretch ]? font-size font-family` Example `set input-font-normal=bold 10pt "DejaVu Sans Mono"` instead of previous `set input-fg-normal=DejaVu Sans Mono Bold 10` +* Renames some settings to consequently use dashed setting names. Following + settings where changed. + ``` + previous setting - new setting name + -------------------------------------- + hintkeys - hint-keys + cursivfont - cursiv-font + defaultfont - default-font + fontsize - font-size + minimumfontsize - minimum-font-size + monofont - monospace-font + monofontsize - monospace-font-size + offlinecache - offline-cache + sansfont - sans-serif-font + seriffont - serif-font + xssauditor - xss-auditor + userscripts - user-scripts + scrollstep - scroll-step + statusbar - status-bar + ``` ### Removed -* `FEATURE_COOKIE` precompiler flag was removed because compiling without cookie - support does not bring any real benefit +* There where many features removed during the webkit2 migration. + TODO list removed features and if they will be added again + +--- ## [2.12] - 2017-04-11 @@ -75,6 +98,6 @@ cookie file * Fixed none POSIX `echo -n` call -[unreleased]: https://github.com/fanglingsu/vimb/compare/2.11...HEAD +[unreleased]: https://github.com/fanglingsu/vimb/compare/2.12...webkit2 [2.12]: https://github.com/fanglingsu/vimb/compare/2.11...2.12 [2.11]: https://github.com/fanglingsu/vimb/compare/2.10...2.11 From 51d2b9c017ae435a6773c4c2f64209e7842feb98 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 19 May 2017 01:47:58 +0200 Subject: [PATCH 179/201] Mention runsandbox target in README. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6d8124ff..32a87446 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ root). make make install +To run vimb without installation for testing it out use the 'runsandbox' make +target. + + make runsandbox + ## Mailing list - feature requests, issues and patches can be discussed on the [mailing list][mail] From afe335d84071b088d44450156e24daec6bc935cd Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 19 May 2017 10:05:55 +0200 Subject: [PATCH 180/201] adds incsearch to manpage (#365) --- doc/vimb.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 3dd694f0..e5688eca 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -943,6 +943,9 @@ Enable or disable support for . .B images (bool) Determines whether images should be automatically loaded or not. .TP +.B incsearch (bool) +While typing a search command, show where the pattern typed so far matches. +.TP .B input-autohide (bool) If enabled the inputbox will be hidden whenever it contains no text. .TP From 445a420f4d16fe863744d5813496fecdcd62af86 Mon Sep 17 00:00:00 2001 From: Robert Timm Date: Fri, 19 May 2017 14:16:08 +0200 Subject: [PATCH 181/201] adds insight about shortcut-default #365 --- doc/vimb.1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index e5688eca..32594214 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -577,9 +577,10 @@ contain spaces like `:open map "city hall, London" railway station, London' 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. +Set the shortcut for given \fIshortcut\fP as the default, that is the shortcut +to be used if no shortcut is given and the string to open is not an URI. 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 From f8ea05f827ca2609ac2b1bb229acf67277703289 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 19 May 2017 23:55:06 +0200 Subject: [PATCH 182/201] Move include of hints.js.h to the right place. --- src/hints.c | 1 - src/setting.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hints.c b/src/hints.c index d5f5f40a..763e8687 100644 --- a/src/hints.c +++ b/src/hints.c @@ -27,7 +27,6 @@ #include "main.h" #include "ascii.h" #include "command.h" -#include "hints.js.h" #include "input.h" #include "map.h" #include "ext-proxy.h" diff --git a/src/setting.c b/src/setting.c index de33832a..2e1292f2 100644 --- a/src/setting.c +++ b/src/setting.c @@ -23,6 +23,7 @@ #include "completion.h" #include "config.h" #include "ext-proxy.h" +#include "hints.js.h" #include "main.h" #include "setting.h" #include "scripts/scripts.h" From 6ff33e957e309f916a39ccb7af49e797125f01e0 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sat, 20 May 2017 00:31:56 +0200 Subject: [PATCH 183/201] Cast to OptArgFunction. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index fd77f004..b0e34f1e 100644 --- a/src/main.c +++ b/src/main.c @@ -1744,7 +1744,7 @@ int main(int argc, char* argv[]) 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}, - {"profile", 'p', 0, G_OPTION_ARG_CALLBACK, profileOptionArgFunc, "Profile name", NULL}, + {"profile", 'p', 0, G_OPTION_ARG_CALLBACK, (GOptionArgFunc*)profileOptionArgFunc, "Profile name", NULL}, {"version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Print version", NULL}, {NULL} }; From 34233193cba4bf1b0e91f3210660e20fc58e0be8 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 10:36:40 +0200 Subject: [PATCH 184/201] setting: always clear scripts previous to adding global ones Previous to adding user scripts to the content manager, we should always clear existing scripts. Otherwise, e.g. in the sequence :set user-scripts=off :set user-scripts=on we'd end up with the same global scripts injected twice. Fix the issue by unconditionally clearing all user scripts previous to adding scripts. --- src/setting.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/setting.c b/src/setting.c index de33832a..ee7cb19b 100644 --- a/src/setting.c +++ b/src/setting.c @@ -607,6 +607,7 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, gboolean enabled = *(gboolean*)value; ucm = webkit_web_view_get_user_content_manager(c->webview); + webkit_user_content_manager_remove_all_scripts(ucm); if (enabled) { if (vb.files[FILES_SCRIPT] @@ -621,8 +622,6 @@ static int user_scripts(Client *c, const char *name, DataType type, void *value, webkit_user_script_unref(script); g_free(source); } - } else { - webkit_user_content_manager_remove_all_scripts(ucm); } /* Inject the global scripts. */ From 99f5a141bb26c771d18f238260f1b7d00e7b3447 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 10:42:40 +0200 Subject: [PATCH 185/201] hints: remove unused hints.js.h file The "hints.js.h" file has been superseded by "scripts.h", which is generated by make from our JavaScript and CSS files. As such, we can remove the unused "hints.js.h" file and remove the include from "hints.c". --- src/hints.c | 1 - src/hints.js.h | 1 - 2 files changed, 2 deletions(-) delete mode 100644 src/hints.js.h diff --git a/src/hints.c b/src/hints.c index d5f5f40a..763e8687 100644 --- a/src/hints.c +++ b/src/hints.c @@ -27,7 +27,6 @@ #include "main.h" #include "ascii.h" #include "command.h" -#include "hints.js.h" #include "input.h" #include "map.h" #include "ext-proxy.h" diff --git a/src/hints.js.h b/src/hints.js.h deleted file mode 100644 index 102d9c6b..00000000 --- a/src/hints.js.h +++ /dev/null @@ -1 +0,0 @@ -#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;ioffsets.bottom||rect.bottomoffsets.right||rect.right=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;i0);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=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=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,};})());" From 0b0060d95885f280bc635ea24d73078880951b29 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 12:25:25 +0200 Subject: [PATCH 186/201] setting: expose hardware acceleration settings Previous to the migration to the WebKit 2 API, we were exposing settings to enable hardware acceleration for compositing via "accelerated-compositing". With WebKit 2, there are two relevant settings to enable compositing. The "hardware-acceleration_policy" is used to determine when the rendering process should actually use hardware acceleration. "accelerated-2d-canvas" enables hardware acceleration for some 2D canvas content if WebKitGTK+ is built with Cairo and its unstable CairoGL API. Thes patch exposes both of these settings via vimb's settings engine. --- src/setting.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/setting.c b/src/setting.c index 2e1292f2..a34e42c7 100644 --- a/src/setting.c +++ b/src/setting.c @@ -54,6 +54,7 @@ static int cookie_accept(Client *c, const char *name, DataType type, void *value static int default_zoom(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 gui_style(Client *c, const char *name, DataType type, void *value, void *data); +static int hardware_acceleration_policy(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); @@ -81,6 +82,7 @@ void setting_init(Client *c) setting_add(c, "useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/603.1 (KHTML, like Gecko) " PROJECT "/" VERSION " Version/10.0 Safari/603.1", webkit, 0, "user-agent"); /* TODO use the real names for webkit settings */ i = 14; + setting_add(c, "accelerated-2d-canvas", TYPE_BOOLEAN, &off, webkit, 0, "enable-accelerated-2d-canvas"); setting_add(c, "caret", TYPE_BOOLEAN, &off, webkit, 0, "enable-caret-browsing"); setting_add(c, "cursiv-font", TYPE_CHAR, &"serif", webkit, 0, "cursive-font-family"); setting_add(c, "default-charset", TYPE_CHAR, &"utf-8", webkit, 0, "default-charset"); @@ -89,6 +91,7 @@ void setting_init(Client *c) i = SETTING_DEFAULT_FONT_SIZE; setting_add(c, "font-size", TYPE_INTEGER, &i, webkit, 0, "default-font-size"); setting_add(c, "frame-flattening", TYPE_BOOLEAN, &off, webkit, 0, "enable-frame-flattening"); + setting_add(c, "hardware-acceleration-policy", TYPE_CHAR, &"ondemand", hardware_acceleration_policy, FLAG_NODUP, NULL); setting_add(c, "header", TYPE_CHAR, &"", headers, FLAG_LIST|FLAG_NODUP, "header"); i = 1000; setting_add(c, "hint-timeout", TYPE_INTEGER, &i, NULL, 0, NULL); @@ -540,6 +543,24 @@ static int fullscreen(Client *c, const char *name, DataType type, void *value, v return CMD_SUCCESS; } +static int hardware_acceleration_policy(Client *c, const char *name, DataType type, void *value, void *data) +{ + WebKitSettings *settings = webkit_web_view_get_settings(c->webview); + + if (g_str_equal(value, "ondemand")) { + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND); + } else if (g_str_equal(value, "always")) { + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS); + } else if (g_str_equal(value, "never")) { + webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER); + } else { + vb_echo(c, MSG_ERROR, TRUE, "%s must be in [ondemand, always, never]", name); + return CMD_ERROR|CMD_KEEPINPUT; + } + + return CMD_SUCCESS; +} + /** * Allow to set user defined http headers. * From 1304b8cfc8408c005b37c0416013ed2148519cba Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 17:35:44 +0200 Subject: [PATCH 187/201] scroll: fix percent-based scrolling Scrolling via ^D and ^U should scroll by a percentage of the client's current viewport. We do this in JavaScript via the client height of the document's element. Accidentally, it may hold the desired value in some cases, but in general it only holds the total height of the element itself. What we desire instead is the window's height, which can be retrieved via `Window.innerHeight`. Like this, our calculations are not based upon the webpage but instead on the browser window, fixing scrolling on some pages. --- src/scripts/scroll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/scroll.js b/src/scripts/scroll.js index 92fc4818..83e30f89 100644 --- a/src/scripts/scroll.js +++ b/src/scripts/scroll.js @@ -2,7 +2,7 @@ function vbscroll(mode, scrollStep, count) { var w = window, d = document, x = y = 0, - ph = d.documentElement.clientHeight, + ph = w.innerHeight, c = count||1, rel = true; switch (mode) { From 841cb5f34af269264a755ee35d7a372e17807526 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 18:49:08 +0200 Subject: [PATCH 188/201] hints: use `window.location.href` to change current URL When opening a hint in the current window, we do so by emulating a `click()` on the element that we want to switch to. This enables us to open hints when there is no href set, e.g. when the element executes JavaScript to change the page. Unfortunately, this method of opening links does not work when JavaScript is disabled in the client. If the element has a `href` attribute, we can work around this problem by directly setting `window.location.href` to the element's reference. Next to being a more obvious method of setting the current URL, this also fixes hints with JavaScript disabled. Note that we still have to keep around the old method of using `click()` on the element in case it has no href attribute. --- src/scripts/hints.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 0c136440..ceb86ed9 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -387,15 +387,19 @@ var hints = Object.freeze((function(){ /* We call open() and click() in async mode to avoid return as fast as possible. */ /* If we don't return immediately, the EvalJS dbus call will probably timeout and cause */ /* errors. */ - if (newWin && e.hasAttribute('href')) { - /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's */ - /* target to _blank. Therefore, we can't simulate ctrl-click through _blank like we */ - /* used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a */ - /* simple link. In other cases, we fire the even normally. */ - window.setTimeout(function() { - window.open(e.getAttribute('href'), '_blank'); - }, 0 - ); + if (e.hasAttribute('href')) { + if (newWin) { + /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's */ + /* target to _blank. Therefore, we can't simulate ctrl-click through _blank like we */ + /* used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a */ + /* simple link. In other cases, we fire the even normally. */ + window.setTimeout(function() { + window.open(e.getAttribute('href'), '_blank'); + }, 0 + ); + } else { + window.location.href = e.getAttribute('href'); + } } window.setTimeout(function() {e.click();}, 0); } From 0237d64daed59ad00b64a998c2d619fb78278633 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Sat, 20 May 2017 18:56:18 +0200 Subject: [PATCH 189/201] hints: do not execute `click` in addition to opening hints When the element which we are about to open has a "href" attribute, we will execute either `window.open` or set the `window.location.href` attribute to the target's reference. But as we forgot to call `return` here, we will also execute the code to open elements without "href" attribute. In the case of opening a new window, we will now first open the new window and then also set the current window's URL to the target, which is obviously wrong. Fix the issue by putting the excution of `click()` inside an else-branch. --- src/scripts/hints.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index ceb86ed9..4e8fd28a 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -400,8 +400,9 @@ var hints = Object.freeze((function(){ } else { window.location.href = e.getAttribute('href'); } + } else { + window.setTimeout(function() {e.click();}, 0); } - window.setTimeout(function() {e.click();}, 0); } /* set focus on hint with given index valid hints array */ From b09e6a6379caec45dc540e8d6eeb81cb5e595a3c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 21 May 2017 00:32:20 +0200 Subject: [PATCH 190/201] Don't attempt to open empty url by hinting. There are pages where following links are used .... In this special case of href="#" the elements should be clicked to let webkit decide how to handle the element instead of opening current page with appended location mark. --- src/scripts/hints.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scripts/hints.js b/src/scripts/hints.js index 4e8fd28a..f482bd77 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -387,18 +387,19 @@ var hints = Object.freeze((function(){ /* We call open() and click() in async mode to avoid return as fast as possible. */ /* If we don't return immediately, the EvalJS dbus call will probably timeout and cause */ /* errors. */ - if (e.hasAttribute('href')) { + var href; + if ((href = e.getAttribute('href')) && href != '#') { if (newWin) { /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's */ /* target to _blank. Therefore, we can't simulate ctrl-click through _blank like we */ /* used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a */ /* simple link. In other cases, we fire the even normally. */ window.setTimeout(function() { - window.open(e.getAttribute('href'), '_blank'); + window.open(href, '_blank'); }, 0 ); } else { - window.location.href = e.getAttribute('href'); + window.location.href = href; } } else { window.setTimeout(function() {e.click();}, 0); From 0be14edf9004719890bd4b253c5cc9dd7a2f0e47 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 21 May 2017 01:55:03 +0200 Subject: [PATCH 191/201] Don't handle hint in new window by javascript. There are a lot of restrictions that introduces some hacks to allow to open hints into new window. The hinting script does now only fire the hints in case of ;o and ;t hinting and the decision where to open the navigation decision is met in the c layer. --- src/hints.c | 7 +++++++ src/main.c | 10 ++++++++-- src/main.h | 1 + src/scripts/hints.js | 24 ++++++------------------ 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/hints.c b/src/hints.c index 763e8687..6046706b 100644 --- a/src/hints.c +++ b/src/hints.c @@ -277,6 +277,13 @@ static gboolean call_hints_function(Client *c, const char *func, const char* arg if (!hints.gmode && c->mode->id == 'c') { vb_enter(c, 'n'); } + /* If open in new window hinting is use, set a flag on the mode after + * changing to normal mode. This is used in on_webview_decide_policy + * to enforce opening into new instance for the next navigation + * action. */ + if (hints.mode == 't') { + c->mode->flags |= FLAG_NEW_WIN; + } } else if (!strncmp(value, "INSERT:", 7)) { fire_timeout(c, false); vb_enter(c, 'i'); diff --git a/src/main.c b/src/main.c index b0e34f1e..e03aaaa4 100644 --- a/src/main.c +++ b/src/main.c @@ -1141,8 +1141,14 @@ static gboolean on_webview_decide_policy(WebKitWebView *webview, webkit_policy_decision_ignore(dec); return TRUE; } - if (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED - && (button == 2 || (button == 1 && mod & GDK_CONTROL_MASK))) { + /* Spawn new instance if the new win flag is set on the mode, or + * the navigation was triggered by CTRL-LeftMouse or MiddleMouse. */ + if ((c->mode->flags & FLAG_NEW_WIN) + || (webkit_navigation_action_get_navigation_type(a) == WEBKIT_NAVIGATION_TYPE_LINK_CLICKED + && (button == 2 || (button == 1 && mod & GDK_CONTROL_MASK)))) { + + /* Remove the FLAG_NEW_WIN after the first use. */ + c->mode->flags &= ~FLAG_NEW_WIN; webkit_policy_decision_ignore(dec); spawn_new_instance(webkit_uri_request_get_uri(req), TRUE); diff --git a/src/main.h b/src/main.h index 3b7a31ec..d2ac9f14 100644 --- a/src/main.h +++ b/src/main.h @@ -198,6 +198,7 @@ struct Mode { #define FLAG_HINTING 0x0002 /* marks active hinting submode */ #define FLAG_COMPLETION 0x0004 /* marks active completion submode */ #define FLAG_PASSTHROUGH 0x0008 /* don't handle any other keybind than */ +#define FLAG_NEW_WIN 0x0010 /* enforce opening of pages into new window */ unsigned int flags; }; diff --git a/src/scripts/hints.js b/src/scripts/hints.js index f482bd77..724490e9 100644 --- a/src/scripts/hints.js +++ b/src/scripts/hints.js @@ -383,25 +383,14 @@ var hints = Object.freeze((function(){ } /* internal used methods */ - function open(e, newWin) { - /* We call open() and click() in async mode to avoid return as fast as possible. */ - /* If we don't return immediately, the EvalJS dbus call will probably timeout and cause */ - /* errors. */ + function open(e) { var href; if ((href = e.getAttribute('href')) && href != '#') { - if (newWin) { - /* Since the "noopener" vulnerability thing, it's not possible to set an anchor's */ - /* target to _blank. Therefore, we can't simulate ctrl-click through _blank like we */ - /* used to. Therefore, we limit ourselves to "window.open()" in cases we're firing a */ - /* simple link. In other cases, we fire the even normally. */ - window.setTimeout(function() { - window.open(href, '_blank'); - }, 0 - ); - } else { - window.location.href = href; - } + window.location.href = href; } else { + /* We call click() in async mode to return as fast as possible. If + * we don't return immediately, the EvalJS dbus call will probably + * timeout and cause errors. */ window.setTimeout(function() {e.click();}, 0); } } @@ -465,8 +454,7 @@ var hints = Object.freeze((function(){ }, /* 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:";}, + ot: function(e) {open(e); return "DONE:";}, eiIOpPsTxy: function(e) {return "DATA:" + getSrc(e);}, Y: function(e) {return "DATA:" + (e.textContent || "");} }; From e9d986f5bdaac99c34c8fa115436c7d03a0c5368 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Sun, 21 May 2017 01:58:54 +0200 Subject: [PATCH 192/201] Stop hinting when element clicked by mouse. In case a links was clicked by mouse during active hinting, the new page was loaded and kept still in hinting mode but there where no hints shown on the new page. --- src/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.c b/src/main.c index e03aaaa4..0de0da49 100644 --- a/src/main.c +++ b/src/main.c @@ -1206,6 +1206,13 @@ static void on_webview_load_changed(WebKitWebView *webview, c->state.progress = 0; vb_statusbar_update(c); set_title(c, webkit_web_view_get_uri(webview)); + /* Make sure hinting is cleared before the new page is loaded. + * Without that vimb would still be in hinting mode after hinting + * was started and some links was clicked my mouse. Even if there + * could not hints be shown. */ + if (c->mode->flags & FLAG_HINTING) { + vb_enter(c, 'n'); + } break; case WEBKIT_LOAD_REDIRECTED: From d1fa2340dd984dcbb9048a4df8097c2ba6d9a745 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 23 May 2017 22:29:11 +0200 Subject: [PATCH 193/201] Change default user-agent. Use the user-agent that would be created by webkit_settings_set_user_agent_with_application_details. --- src/setting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setting.c b/src/setting.c index ab364278..e3cabdd4 100644 --- a/src/setting.c +++ b/src/setting.c @@ -78,7 +78,7 @@ void setting_init(Client *c) * 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 x86_64) AppleWebKit/603.1 (KHTML, like Gecko) " PROJECT "/" VERSION " Version/10.0 Safari/603.1", webkit, 0, "user-agent"); + setting_add(c, "useragent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 " PROJECT "/" VERSION, webkit, 0, "user-agent"); /* TODO use the real names for webkit settings */ i = 14; setting_add(c, "accelerated-2d-canvas", TYPE_BOOLEAN, &off, webkit, 0, "enable-accelerated-2d-canvas"); From eb99771422a45c3a95b5335157dfe5a6eb70780c Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 23 May 2017 22:45:06 +0200 Subject: [PATCH 194/201] Use dashed naming for user-agent too. --- doc/vimb.1 | 2 +- src/setting.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 32594214..0fc82062 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -1079,7 +1079,7 @@ of a Tab character. The time in milliseconds that is waited for a key code or mapped key sequence to complete. .TP -.B useragent (string) +.B user-agent (string) The user-agent string used by WebKit. .TP .B user-scripts (bool) diff --git a/src/setting.c b/src/setting.c index e3cabdd4..62f7c853 100644 --- a/src/setting.c +++ b/src/setting.c @@ -78,7 +78,7 @@ void setting_init(Client *c) * 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 x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 " PROJECT "/" VERSION, webkit, 0, "user-agent"); + setting_add(c, "user-agent", TYPE_CHAR, &"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 " PROJECT "/" VERSION, webkit, 0, "user-agent"); /* TODO use the real names for webkit settings */ i = 14; setting_add(c, "accelerated-2d-canvas", TYPE_BOOLEAN, &off, webkit, 0, "enable-accelerated-2d-canvas"); From eef415325333b2f5f2b1cbf54041aa8b72c4e2db Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 23 May 2017 23:09:57 +0200 Subject: [PATCH 195/201] Added hardware acceleration to man page. --- doc/vimb.1 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 0fc82062..70c92726 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -809,6 +809,11 @@ comparison for further typed chars. .SH SETTINGS All settings listed below can be set with the `:set' command. .TP +.B accelerated-2d-canvas (bool) +Enable or disable accelerated 2D canvas. +When accelerated 2D canvas is enabled, WebKit may render some 2D canvas +content using hardware accelerated drawing operations. +.TP .B caret (bool) Whether to enable accessibility enhanced keyboard navigation. .TP @@ -872,6 +877,22 @@ which will flatten all the frames to become one scrollable page. .B fullscreen (bool) Show the current window full-screen. .TP +.B hardware-acceleration-policy (string) +This setting decides how to enable and disable hardware acceleration. +.PD 0 +.RS +.IP - 2 +`ondemand' enables the hardware acceleration when the web contents request it, disabling it again when no +longer needed. +.IP - 2 +`always' enforce hardware acceleration to be enabled. +.IP - 2 +`never' disables it completely. +Note that disabling hardware acceleration might cause some websites to not +render correctly or consume more CPU. +.RE +.PD +.TP .B header (list) Comma separated list of headers that replaces default header sent by WebKit or new headers. From 4a472a46d044c058e11fc59b7f2c81ada5820d79 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Tue, 23 May 2017 23:17:40 +0200 Subject: [PATCH 196/201] Sort changed settings. --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac3a7b91..50d64663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,20 +14,21 @@ ``` previous setting - new setting name -------------------------------------- - hintkeys - hint-keys cursivfont - cursiv-font defaultfont - default-font fontsize - font-size + hintkeys - hint-keys minimumfontsize - minimum-font-size monofont - monospace-font monofontsize - monospace-font-size offlinecache - offline-cache + useragent - user-agent sansfont - sans-serif-font - seriffont - serif-font - xssauditor - xss-auditor - userscripts - user-scripts scrollstep - scroll-step + seriffont - serif-font statusbar - status-bar + userscripts - user-scripts + xssauditor - xss-auditor ``` ### Removed From 164e9399f858093d476b267e6a28ab2098e57d8a Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 24 May 2017 20:42:30 +0200 Subject: [PATCH 197/201] Removed hard coded tls policy. The policy is already set during setting initialization. --- src/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.c b/src/main.c index 0de0da49..d59f796b 100644 --- a/src/main.c +++ b/src/main.c @@ -1536,9 +1536,6 @@ static void vimb_setup(void) WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT); } - /* TODO move to settings.c */ - webkit_web_context_set_tls_errors_policy(ctx, TRUE ? WEBKIT_TLS_ERRORS_POLICY_FAIL : WEBKIT_TLS_ERRORS_POLICY_IGNORE); - /* 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); From 6eb433849e5d70cb0202f158d10ae9a67a6cfdec Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 24 May 2017 21:04:32 +0200 Subject: [PATCH 198/201] Add :clearcache command. This allows to discard all caches currently used by webkit. --- doc/vimb.1 | 4 ++++ src/ex.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/doc/vimb.1 b/doc/vimb.1 index 70c92726..306d0f1b 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -630,6 +630,10 @@ queue. Removes all entries from queue. .SS Misc .TP +.B :cl[earcache] +Clears all resources currently cached by webkit. +Note that this effects all running instances of vimb. +.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' diff --git a/src/ex.c b/src/ex.c index 3660ec75..d924ae07 100644 --- a/src/ex.c +++ b/src/ex.c @@ -48,6 +48,7 @@ typedef enum { EX_BMR, EX_EVAL, EX_HARDCOPY, + EX_CLEARCACHE, EX_CMAP, EX_CNOREMAP, EX_HANDADD, @@ -129,6 +130,7 @@ 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 void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, Client *c); +static VbCmdResult ex_clearcache(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); @@ -164,6 +166,7 @@ static ExInfo commands[] = { {"cmap", EX_CMAP, ex_map, EX_FLAG_LHS|EX_FLAG_CMD}, {"cnoremap", EX_CNOREMAP, ex_map, EX_FLAG_LHS|EX_FLAG_CMD}, {"cunmap", EX_CUNMAP, ex_unmap, EX_FLAG_LHS}, + {"clearcache", EX_CLEARCACHE, ex_clearcache, EX_FLAG_NONE}, {"hardcopy", EX_HARDCOPY, ex_hardcopy, EX_FLAG_NONE}, {"handler-add", EX_HANDADD, ex_handlers, EX_FLAG_RHS}, {"handler-remove", EX_HANDREM, ex_handlers, EX_FLAG_RHS}, @@ -822,6 +825,12 @@ static void on_eval_script_finished(GDBusProxy *proxy, GAsyncResult *result, Cli } } +static VbCmdResult ex_clearcache(Client *c, const ExArg *arg) +{ + webkit_web_context_clear_cache(webkit_web_context_get_default()); + return CMD_SUCCESS; +} + static VbCmdResult ex_hardcopy(Client *c, const ExArg *arg) { WebKitPrintOperation *op = webkit_print_operation_new(c->webview); From 773a71ca67dc3b637ee5bb578175c10914690574 Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Wed, 24 May 2017 21:43:02 +0200 Subject: [PATCH 199/201] Set window default size. --- src/config.def.h | 3 +++ src/main.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/config.def.h b/src/config.def.h index 44f9351f..cdb6ed59 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -52,3 +52,6 @@ #define SETTING_HOME_PAGE "about:blank" #define MAXIMUM_HINTS 500 +/* default window dimensions */ +#define WIN_WIDTH 800 +#define WIN_HEIGHT 600 diff --git a/src/main.c b/src/main.c index d59f796b..75affa84 100644 --- a/src/main.c +++ b/src/main.c @@ -682,6 +682,8 @@ static Client *client_new(WebKitWebView *webview, gboolean show) (int)GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(c->window)))); } + gtk_window_set_default_size(GTK_WINDOW(c->window), WIN_WIDTH, WIN_HEIGHT); + completion_init(c); map_init(c); From fbeb327bf26980bbeea5acc3dfe5b4b36f512edf Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 26 May 2017 23:34:17 +0200 Subject: [PATCH 200/201] Removed no_proxy from man. This is not done by vimb at the time because the proxy stuff is done by webkit itself. --- doc/vimb.1 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 306d0f1b..ef1b81cf 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -1,4 +1,5 @@ .\" vim: ft=groff +.ss 12 0 .ad l .TH VIMB 1 "DATE" "vimb/VERSION" "Vimb Manual" .de EX @@ -986,7 +987,7 @@ Whether JavaScript can access the clipboard. .TP .B javascript-can-open-windows-automatically (bool) Whether JavaScript can open popup windows automatically without user -intervention. +interaction. .TP .B media-playback-allows-inline (bool) Whether media playback is full-screen only or inline playback is allowed. @@ -1189,13 +1190,6 @@ PREFIX/share/vimb/examples. 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 From 6ce3e09420ed05ed171c34e9fefcad7db07eeb0e Mon Sep 17 00:00:00 2001 From: Daniel Carl Date: Fri, 26 May 2017 23:54:51 +0200 Subject: [PATCH 201/201] Update license year. --- src/ascii.h | 2 +- src/command.c | 2 +- src/command.h | 2 +- src/completion.c | 2 +- src/completion.h | 2 +- src/config.def.h | 2 +- src/ex.c | 2 +- src/ex.h | 2 +- src/ext-proxy.c | 2 +- src/ext-proxy.h | 2 +- src/hints.h | 2 +- src/history.c | 2 +- src/history.h | 2 +- src/input.c | 2 +- src/input.h | 2 +- src/js.c | 2 +- src/js.h | 2 +- src/main.c | 2 +- src/main.h | 2 +- src/map.c | 2 +- src/map.h | 2 +- src/normal.c | 2 +- src/normal.h | 2 +- src/setting.c | 2 +- src/setting.h | 2 +- src/shortcut.c | 2 +- src/shortcut.h | 2 +- src/util.c | 2 +- src/util.h | 2 +- src/webextension/ext-dom.c | 2 +- src/webextension/ext-dom.h | 2 +- src/webextension/ext-main.c | 2 +- src/webextension/ext-main.h | 2 +- src/webextension/ext-util.c | 2 +- src/webextension/ext-util.h | 2 +- 35 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ascii.h b/src/ascii.h index d85ef688..93027881 100644 --- a/src/ascii.h +++ b/src/ascii.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/command.c b/src/command.c index 88962477..28e9a12f 100644 --- a/src/command.c +++ b/src/command.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/command.h b/src/command.h index 16a5d25a..d7625251 100644 --- a/src/command.h +++ b/src/command.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/completion.c b/src/completion.c index 22fced68..4042cb8d 100644 --- a/src/completion.c +++ b/src/completion.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/completion.h b/src/completion.h index 8c8d2286..df8eb60a 100644 --- a/src/completion.h +++ b/src/completion.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/config.def.h b/src/config.def.h index cdb6ed59..48f33924 100644 --- a/src/config.def.h +++ b/src/config.def.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/ex.c b/src/ex.c index d924ae07..b0a4480f 100644 --- a/src/ex.c +++ b/src/ex.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/ex.h b/src/ex.h index 1fd057f2..44ac11f1 100644 --- a/src/ex.h +++ b/src/ex.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/ext-proxy.c b/src/ext-proxy.c index f1e42e5d..6b9c9578 100644 --- a/src/ext-proxy.c +++ b/src/ext-proxy.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/ext-proxy.h b/src/ext-proxy.h index 82e706e3..c5f41d1e 100644 --- a/src/ext-proxy.h +++ b/src/ext-proxy.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/hints.h b/src/hints.h index 6821554c..a64d689e 100644 --- a/src/hints.h +++ b/src/hints.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/history.c b/src/history.c index b2bb1d47..e6cfc73b 100644 --- a/src/history.c +++ b/src/history.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/history.h b/src/history.h index c074d062..46df5328 100644 --- a/src/history.h +++ b/src/history.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/input.c b/src/input.c index f0ece141..7bd8face 100644 --- a/src/input.c +++ b/src/input.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/input.h b/src/input.h index 29a07995..eaa0169f 100644 --- a/src/input.h +++ b/src/input.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/js.c b/src/js.c index a65c41c2..420a0597 100644 --- a/src/js.c +++ b/src/js.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/js.h b/src/js.h index fd6158b5..76e73e50 100644 --- a/src/js.h +++ b/src/js.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/main.c b/src/main.c index 75affa84..5fc4e203 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/main.h b/src/main.h index d2ac9f14..ac565d4e 100644 --- a/src/main.h +++ b/src/main.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/map.c b/src/map.c index 7cf92042..9f2a5ea7 100644 --- a/src/map.c +++ b/src/map.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/map.h b/src/map.h index d97d1b42..5faf9092 100644 --- a/src/map.h +++ b/src/map.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/normal.c b/src/normal.c index b66948a9..0dc06ee3 100644 --- a/src/normal.c +++ b/src/normal.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/normal.h b/src/normal.h index 912d2c64..426ecb45 100644 --- a/src/normal.h +++ b/src/normal.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/setting.c b/src/setting.c index 62f7c853..1224ae95 100644 --- a/src/setting.c +++ b/src/setting.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/setting.h b/src/setting.h index 93f7182c..584e307f 100644 --- a/src/setting.h +++ b/src/setting.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/shortcut.c b/src/shortcut.c index 4e83e4b8..a4205022 100644 --- a/src/shortcut.c +++ b/src/shortcut.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/shortcut.h b/src/shortcut.h index 21e21ff1..0b4cabe5 100644 --- a/src/shortcut.h +++ b/src/shortcut.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/util.c b/src/util.c index 38f5d98d..e5a813b5 100644 --- a/src/util.c +++ b/src/util.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/util.h b/src/util.h index 9043eb23..7e802e46 100644 --- a/src/util.h +++ b/src/util.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-dom.c b/src/webextension/ext-dom.c index 7c0b7cd6..6f9030f0 100644 --- a/src/webextension/ext-dom.c +++ b/src/webextension/ext-dom.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-dom.h b/src/webextension/ext-dom.h index 795754c4..0dbc0b96 100644 --- a/src/webextension/ext-dom.h +++ b/src/webextension/ext-dom.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-main.c b/src/webextension/ext-main.c index d7ff217b..d70db003 100644 --- a/src/webextension/ext-main.c +++ b/src/webextension/ext-main.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-main.h b/src/webextension/ext-main.h index ff3f0a30..5f829e88 100644 --- a/src/webextension/ext-main.h +++ b/src/webextension/ext-main.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-util.c b/src/webextension/ext-util.c index bdf55d42..c15b6318 100644 --- a/src/webextension/ext-util.c +++ b/src/webextension/ext-util.c @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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 diff --git a/src/webextension/ext-util.h b/src/webextension/ext-util.h index 2629dbcc..d65677c6 100644 --- a/src/webextension/ext-util.h +++ b/src/webextension/ext-util.h @@ -1,7 +1,7 @@ /** * vimb - a webkit based vim like browser. * - * Copyright (C) 2012-2016 Daniel Carl + * Copyright (C) 2012-2017 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