aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--README1
-rw-r--r--docs/formfiller-data-format35
-rw-r--r--examples/config/config11
-rw-r--r--examples/data/scripts/follow.js416
-rwxr-xr-xexamples/data/scripts/follow.sh20
-rw-r--r--examples/data/scripts/formfiller.js67
-rwxr-xr-xexamples/data/scripts/formfiller.sh324
-rw-r--r--examples/data/style.css38
-rw-r--r--extras/vim/syntax/uzbl.vim13
-rw-r--r--src/callbacks.c85
-rw-r--r--src/callbacks.h6
-rw-r--r--src/events.c7
-rw-r--r--src/events.h5
-rw-r--r--src/io.c11
-rw-r--r--src/util.c176
-rw-r--r--src/util.h6
-rw-r--r--src/uzbl-core.c34
-rw-r--r--src/uzbl-core.h14
-rw-r--r--tests/test-command.c8
20 files changed, 667 insertions, 611 deletions
diff --git a/AUTHORS b/AUTHORS
index 5e7ef07..ff31818 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -75,6 +75,7 @@ In alphabetical order:
Simon Lipp (sloonz) - various patches, EM contributions
Sylvester Johansson (scj) - form filler script & different take on link follower
Tassilo Horn (tsdh) - $VISUAL patch
+ Taylan Ulrich Bayırlı (taylanub) - updated form filler
Thorsten Wilms - logo design
Tom Adams (holizz) - few patches, cookies.py, gtkplug/socket & proof of concept uzbl_tabbed.py, scheme_handler
Uli Schlachter (psychon) - basic mime_policy_cb & Makefile patch
diff --git a/README b/README
index d20cbd2..3ac9a28 100644
--- a/README
+++ b/README
@@ -356,6 +356,7 @@ file).
stylesheet.
* `resizable_text_areas`: Whether text areas can be resized (default 0).
* `default_encoding`: The default text encoding (default "iso-8859-1").
+* `current_encoding`: This can be set to force a text encoding.
* `enforce_96_dpi`: Enforce a resolution of 96 DPI (default 1).
* `caret_browsing`: Whether the caret is enabled in the text portion of pages
(default 0).
diff --git a/docs/formfiller-data-format b/docs/formfiller-data-format
new file mode 100644
index 0000000..f42114c
--- /dev/null
+++ b/docs/formfiller-data-format
@@ -0,0 +1,35 @@
+FORMFILLER FILE FORMAT
+
+lines starting with '>' are ignored
+
+a file consists of profile definitions
+lines between profile definitions are ignored
+
+a line starting with '!profile=' introduces a profile definition
+the rest of that line is taken as the profile name
+profile names must match the RE /^[a-zA-Z0-9_-]*$/
+a line starting with '!' terminates the profile definition
+the rest of that line is ignored
+
+a profile definition consists of field definitions
+lines between field definitions are ignored
+
+a line starting with '%' introduces a field definition
+(details depend on the field type)
+the rest of that line shall be in the format ...
+ 'name(type){value}:checked' for checkbox/radio
+ 'name(type):value' for text/password/search
+ 'name(type):' for textarea
+where name/type/value/checked are the input field's HTML attribute values,
+ 'name' always being percent encoded, 'checked' being a JS boolean,
+ and 'value' being percent encoded if type is checkbox/radio
+for all types but textarea, the field definition ends with that single line
+for textarea, all lines up to (but excluding) one starting with '%' make up the 'value'
+the rest of that line starting with '%' is ignored
+
+for all lines, a leading '\', if present, is removed
+
+--
+
+a once-edit temporary file consists only of field definitions,
+ and lines starting with '!' or '>' are not special
diff --git a/examples/config/config b/examples/config/config
index d5555e0..bcd6d3e 100644
--- a/examples/config/config
+++ b/examples/config/config
@@ -69,6 +69,11 @@ set download_handler = sync_spawn @scripts_dir/download.sh
# Load commit handlers
@on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
+ # add some javascript to the page for other 'js' and 'script' commands to access later.
+@on_event LOAD_COMMIT js uzbl = {};
+@on_event LOAD_COMMIT script @scripts_dir/formfiller.js
+@on_event LOAD_COMMIT script @scripts_dir/follow.js
+
# Userscripts/per-site-settings. See the script and the example configuration for details
#@on_event LOAD_COMMIT spawn @scripts_dir/per-site-settings.py @data_home/uzbl/per-site-settings
@@ -89,6 +94,9 @@ set download_handler = sync_spawn @scripts_dir/download.sh
# === Behaviour and appearance ===============================================
+# Custom CSS can be defined here, including link follower hint styles
+set stylesheet_uri = file://@data_home/uzbl/style.css
+
set show_status = 1
set status_top = 0
set status_background = #303030
@@ -323,7 +331,8 @@ set follow_hint_keys = 0123456789
#set follow_hint_keys = qwerty
#set follow_hint_keys = asdfghjkl;
#set follow_hint_keys = thsnd-rcgmvwb/;789aefijkopquxyz234
-@cbind fl* = spawn @scripts_dir/follow.sh "%s"
+@cbind fl* = spawn @scripts_dir/follow.sh \@< uzbl.follow("\@follow_hint_keys", "%s", 0) >\@
+@cbind Fl* = spawn @scripts_dir/follow.sh \@< uzbl.follow("\@follow_hint_keys", "%s", 1) >\@
@cbind gi = spawn @scripts_dir/go_input.sh
# Form filler binds
diff --git a/examples/data/scripts/follow.js b/examples/data/scripts/follow.js
index d995696..536256b 100644
--- a/examples/data/scripts/follow.js
+++ b/examples/data/scripts/follow.js
@@ -1,219 +1,192 @@
/* This is the basic linkfollowing script.
- * Its pretty stable, and you configure which keys to use for hinting
*
- * TODO: Some pages mess around a lot with the zIndex which
- * lets some hints in the background.
- * TODO: Some positions are not calculated correctly (mostly
- * because of uber-fancy-designed-webpages. Basic HTML and CSS
- * works good
- * TODO: Still some links can't be followed/unexpected things
- * happen. Blame some freaky webdesigners ;)
+ * TODO:
+ * Some pages mess around a lot with the zIndex which
+ * lets some hints in the background.
+ * Some positions are not calculated correctly (mostly
+ * because of uber-fancy-designed-webpages. Basic HTML and CSS
+ * works good
+ * Still some links can't be followed/unexpected things
+ * happen. Blame some freaky webdesigners ;)
*/
-//Just some shortcuts and globals
-var uzblid = 'uzbl_link_hint';
-var uzbldivid = uzblid + '_div_container';
-var doc = document;
-var win = window;
-var links = document.links;
-var forms = document.forms;
-//Make onlick-links "clickable"
-try {
- HTMLElement.prototype.click = function() {
- if (typeof this.onclick == 'function') {
- this.onclick({
- type: 'click'
- });
- }
- };
-} catch(e) {}
-//Catch the ESC keypress to stop linkfollowing
-function keyPressHandler(e) {
- var kC = window.event ? event.keyCode: e.keyCode;
- var Esc = window.event ? 27 : e.DOM_VK_ESCAPE;
- if (kC == Esc) {
- removeAllHints();
- }
+// Globals
+uzbldivid = 'uzbl_link_hints';
+
+uzbl.follow = function() {
+ // Export
+ charset = arguments[0];
+ newwindow = arguments[2];
+
+ var keypress = arguments[1];
+ return arguments.callee.followLinks(keypress);
+}
+
+uzbl.follow.isFrame = function(el) {
+ return (el.tagName == "FRAME" || el.tagName == "IFRAME");
+}
+
+// find the document that the given element belongs to
+uzbl.follow.getDocument = function(el) {
+ if (this.isFrame(el))
+ return el.contentDocument;
+
+ var doc = el;
+ while (doc.parentNode !== null)
+ doc = doc.parentNode;
+ return doc;
+}
+
+// find all documents in the display, searching frames recursively
+uzbl.follow.documents = function() {
+ return this.windows().map(function(w) { return w.document; }).filter(function(d) { return d != null; });
+}
+
+// find all windows in the display, searching for frames recursively
+uzbl.follow.windows = function(w) {
+  w = (typeof w == 'undefined') ? window.top : w;
+
+ var wins = [w];
+ var frames = w.frames;
+ for(var i = 0; i < frames.length; i++)
+ wins = wins.concat(uzbl.follow.windows(frames[i]));
+ return wins;
+}
+
+// search all frames for elements matching the given CSS selector
+uzbl.follow.query = function(selector) {
+ var res = [];
+ this.documents().forEach(function (doc) {
+ var set = doc.body.querySelectorAll(selector);
+ // convert the NodeList to an Array
+ set = Array.prototype.slice.call(set);
+ res = res.concat(set);
+ });
+ return res;
}
-//Calculate element position to draw the hint
-//Pretty accurate but on fails in some very fancy cases
-function elementPosition(el) {
- var up = el.offsetTop;
- var left = el.offsetLeft;
- var width = el.offsetWidth;
+
+// Calculate element position to draw the hint
+uzbl.follow.elementPosition = function(el) {
+ // el.getBoundingClientRect is another way to do this, but when a link is
+ // line-wrapped we want our hint at the left end of the link, not its
+ // bounding rectangle
+ var up = el.offsetTop;
+ var left = el.offsetLeft;
+ var width = el.offsetWidth;
var height = el.offsetHeight;
+
while (el.offsetParent) {
el = el.offsetParent;
up += el.offsetTop;
left += el.offsetLeft;
}
+
return [up, left, width, height];
}
-//Calculate if an element is visible
-function isVisible(el) {
- if (el == doc) {
- return true;
- }
- if (!el) {
- return false;
- }
- if (!el.parentNode) {
- return false;
- }
- if (el.style) {
- if (el.style.display == 'none') {
- return false;
- }
- if (el.style.visibility == 'hidden') {
- return false;
- }
- }
- return isVisible(el.parentNode);
-}
-//Calculate if an element is on the viewport.
-function elementInViewport(el) {
- offset = elementPosition(el);
- var up = offset[0];
- var left = offset[1];
- var width = offset[2];
+
+// Calculate if an element is on the viewport.
+uzbl.follow.elementInViewport = function(el) {
+ offset = uzbl.follow.elementPosition(el);
+ var up = offset[0];
+ var left = offset[1];
+ var width = offset[2];
var height = offset[3];
- return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset;
+ return up < window.pageYOffset + window.innerHeight &&
+ left < window.pageXOffset + window.innerWidth &&
+ (up + height) > window.pageYOffset &&
+ (left + width) > window.pageXOffset;
}
-//Removes all hints/leftovers that might be generated
-//by this script.
-function removeAllHints() {
+
+// Removes all hints/leftovers that might be generated
+// by this script.
+uzbl.follow.removeAllHints = function(doc) {
var elements = doc.getElementById(uzbldivid);
- if (elements) {
- elements.parentNode.removeChild(elements);
- }
+ if (elements) elements.parentNode.removeChild(elements);
}
-//Generate a hint for an element with the given label
-//Here you can play around with the style of the hints!
-function generateHint(el, label) {
- var pos = elementPosition(el);
- var hint = doc.createElement('div');
- hint.setAttribute('name', uzblid);
+
+// Generate a hint for an element with the given label
+// Here you can play around with the style of the hints!
+uzbl.follow.generateHint = function(doc, el, label, top, left) {
+ var hint = doc.createElement('span');
hint.innerText = label;
- hint.style.display = 'inline';
- hint.style.backgroundColor = '#B9FF00';
- hint.style.border = '2px solid #4A6600';
- hint.style.color = 'black';
- hint.style.fontSize = '9px';
- hint.style.fontWeight = 'bold';
- hint.style.lineHeight = '9px';
- hint.style.margin = '0px';
- hint.style.width = 'auto'; // fix broken rendering on w3schools.com
- hint.style.padding = '1px';
hint.style.position = 'absolute';
- hint.style.zIndex = '1000';
- // hint.style.textTransform = 'uppercase';
- hint.style.left = pos[1] + 'px';
- hint.style.top = pos[0] + 'px';
- // var img = el.getElementsByTagName('img');
- // if (img.length > 0) {
- // hint.style.top = pos[1] + img[0].height / 2 - 6 + 'px';
- // }
- hint.style.textDecoration = 'none';
- // hint.style.webkitBorderRadius = '6px'; // slow
- // Play around with this, pretty funny things to do :)
- // hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px,-5px)';
+ hint.style.top = top + 'px';
+ hint.style.left = left + 'px';
return hint;
}
-// Here we choose what to do with an element that the user has selected.
-// Form elements get selected and/or focussed, and links and buttons are
-// clicked. This function returns "XXXRESET_MODEXXX" to indicate that uzbl
-// should be reset to command mode with an empty keycmd, or
-// "XXX_EMIT_FORM_ACTIVEXXX" to indicate that uzbl should be set to insert mode.
-function clickElem(item) {
- removeAllHints();
- if (item) {
- var name = item.tagName;
- if (name == 'BUTTON') {
- item.click();
- return "XXXRESET_MODEXXX";
- } else if (name == 'INPUT') {
- var type = item.type.toUpperCase();
- if (type == 'TEXT' || type == 'SEARCH' || type == 'PASSWORD') {
- item.focus();
- item.select();
- return "XXXEMIT_FORM_ACTIVEXXX";
- } else {
- item.click();
- return "XXXRESET_MODEXXX";
- }
- } else if (name == 'TEXTAREA' || name == 'SELECT') {
+// Here we choose what to do with an element if we
+// want to "follow" it. On form elements we "select"
+// or pass the focus, on links we try to perform a click,
+// but at least set the href of the link. (needs some improvements)
+uzbl.follow.clickElem = function(item) {
+ if(!item) return;
+ var name = item.tagName;
+
+ if (name == 'INPUT') {
+ var type = item.getAttribute('type').toUpperCase();
+ if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') {
item.focus();
item.select();
return "XXXEMIT_FORM_ACTIVEXXX";
- } else {
- item.click();
- window.location = item.href;
- return "XXXRESET_MODEXXX";
}
+ // otherwise fall through to a simulated mouseclick.
+ } else if (name == 'TEXTAREA' || name == 'SELECT') {
+ item.focus();
+ item.select();
+ return "XXXEMIT_FORM_ACTIVEXXX";
}
+
+ // simulate a mouseclick to activate the element
+ var mouseEvent = document.createEvent("MouseEvent");
+ mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ item.dispatchEvent(mouseEvent);
+ return "XXXRESET_MODEXXX";
}
-//Returns a list of all links (in this version
-//just the elements itself, but in other versions, we
-//add the label here.
-function addLinks() {
- res = [[], []];
- for (var l = 0; l < links.length; l++) {
- var li = links[l];
- if (isVisible(li) && elementInViewport(li)) {
- res[0].push(li);
- }
- }
- return res;
-}
-//Same as above, just for the form elements
-function addFormElems() {
- res = [[], []];
- for (var f = 0; f < forms.length; f++) {
- for (var e = 0; e < forms[f].elements.length; e++) {
- var el = forms[f].elements[e];
- if (el && ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) {
- res[0].push(el);
- }
- }
- }
- return res;
-}
-//Draw all hints for all elements passed. "len" is for
-//the number of chars we should use to avoid collisions
-function reDrawHints(elems, chars) {
- removeAllHints();
- var hintdiv = doc.createElement('div');
- hintdiv.setAttribute('id', uzbldivid);
- for (var i = 0; i < elems[0].length; i++) {
- if (elems[0][i]) {
- var label = elems[1][i].substring(chars);
- var h = generateHint(elems[0][i], label);
- hintdiv.appendChild(h);
- }
- }
- if (document.body) {
- document.body.appendChild(hintdiv);
- }
+
+// Draw all hints for all elements passed.
+uzbl.follow.reDrawHints = function(elems, chars) {
+ var elements = elems.map(function(pair) { return pair[0] });
+ var labels = elems.map(function(pair) { return pair[1].substring(chars) });
+ // we have to calculate element positions before we modify the DOM
+ // otherwise the elementPosition call slows way down.
+ var positions = elements.map(uzbl.follow.elementPosition);
+
+ this.documents().forEach(function(doc) {
+ uzbl.follow.removeAllHints(doc);
+ if (!doc.body) return;
+ doc.hintdiv = doc.createElement('div');
+ doc.hintdiv.id = uzbldivid;
+ if(newwindow) doc.hintdiv.className = "new-window";
+ doc.body.appendChild(doc.hintdiv);
+ });
+
+ elements.forEach(function(el, i) {
+ var label = labels[i];
+ var pos = positions[i];
+ var doc = uzbl.follow.getDocument(el);
+ var h = uzbl.follow.generateHint(doc, el, label, pos[0], pos[1]);
+ doc.hintdiv.appendChild(h);
+ });
}
+
// pass: number of keys
// returns: key length
-function labelLength(n) {
+uzbl.follow.labelLength = function(n) {
var oldn = n;
var keylen = 0;
- if(n < 2) {
- return 1;
- }
- n -= 1; // our highest key will be n-1
+ if(n < 2) return 1;
+ n -= 1; // Our highest key will be n-1
while(n) {
keylen += 1;
n = Math.floor(n / charset.length);
}
return keylen;
}
+
// pass: number
// returns: label
-function intToLabel(n) {
+uzbl.follow.intToLabel = function(n) {
var label = '';
do {
label = charset.charAt(n % charset.length) + label;
@@ -221,55 +194,68 @@ function intToLabel(n) {
} while(n);
return label;
}
+
// pass: label
// returns: number
-function labelToInt(label) {
+uzbl.follow.labelToInt = function(label) {
var n = 0;
- var i;
- for(i = 0; i < label.length; ++i) {
+ for(var i = 0; i < label.length; ++i) {
n *= charset.length;
n += charset.indexOf(label[i]);
}
return n;
}
-//Put it all together
-function followLinks(follow) {
- // if(follow.charAt(0) == 'l') {
- // follow = follow.substr(1);
- // charset = 'thsnlrcgfdbmwvz-/';
- // }
+
+// Put it all together
+uzbl.follow.followLinks = function(follow) {
var s = follow.split('');
- var linknr = labelToInt(follow);
- if (document.body) document.body.setAttribute('onkeyup', 'keyPressHandler(event)');
- var linkelems = addLinks();
- var formelems = addFormElems();
- var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])];
- var len = labelLength(elems[0].length);
- var oldDiv = doc.getElementById(uzbldivid);
- var leftover = [[], []];
- if (s.length == len && linknr < elems[0].length && linknr >= 0) {
- return clickElem(elems[0][linknr]);
- } else {
- for (var j = 0; j < elems[0].length; j++) {
- var b = true;
- var label = intToLabel(j);
- var n = label.length;
- for (n; n < len; n++) {
- label = charset.charAt(0) + label;
- }
- for (var k = 0; k < s.length; k++) {
- b = b && label.charAt(k) == s[k];
- }
- if (b) {
- leftover[0].push(elems[0][j]);
- leftover[1].push(label);
- }
+ var linknr = this.labelToInt(follow);
+
+ var followable = 'a, area, textarea, select, input:not([type=hidden]), button';
+ var uri = 'a, area, frame, iframe';
+ //var focusable = 'a, area, textarea, select, input:not([type=hidden]), button, frame, iframe, applet, object';
+ //var desc = '*[title], img[alt], applet[alt], area[alt], input[alt]';
+ //var image = 'img, input[type=image]';
+
+ if(newwindow)
+ var res = this.query(uri);
+ else
+ var res = this.query(followable);
+
+ var elems = res.filter(uzbl.follow.elementInViewport);
+ var len = this.labelLength(elems.length);
+
+ if (s.length == len && linknr < elems.length && linknr >= 0) {
+ // an element has been selected!
+ var el = elems[linknr];
+
+ // clear all of our hints
+ this.documents().forEach(uzbl.follow.removeAllHints);
+
+ if (newwindow) {
+ // we're opening a new window using the URL attached to this element
+ var uri = el.src || el.href;
+ if(uri.match(/javascript:/)) return;
+ window.open(uri);
+ return "XXXRESET_MODEXXX"
}
- reDrawHints(leftover, s.length);
+
+ // we're just going to click the element
+ return this.clickElem(el);
}
-}
-//Parse input: first argument is follow keys, second is user input.
-var args = '%s'.split(' ');
-var charset = args[0];
-followLinks(args[1]);
+ var leftover = [];
+ for (var j = 0; j < elems.length; j++) {
+ var b = true;
+ var label = this.intToLabel(j);
+ var n = label.length;
+ for (n; n < len; n++)
+ label = charset.charAt(0) + label;
+ for (var k = 0; k < s.length; k++)
+ b = b && label.charAt(k) == s[k];
+ if (b)
+ leftover.push([elems[j], label]);
+ }
+
+ this.reDrawHints(leftover, s.length);
+}
diff --git a/examples/data/scripts/follow.sh b/examples/data/scripts/follow.sh
index d7fe117..1f8947d 100755
--- a/examples/data/scripts/follow.sh
+++ b/examples/data/scripts/follow.sh
@@ -1,21 +1,11 @@
#!/bin/sh
+# This scripts acts on the return value of followLinks in follow.js
-# This script is just a wrapper around follow.js that lets us change uzbl's mode
-# after a link is selected.
-
-# if socat is installed then we can change Uzbl's input mode once a link is
-# selected; otherwise we just select a link.
-if ! which socat >/dev/null 2>&1; then
- echo 'script @scripts_dir/follow.js "@{follow_hint_keys} '$1'"' > "$UZBL_FIFO"
- exit
-fi
-
-result=$(echo 'script @scripts_dir/follow.js "@{follow_hint_keys} '$1'"' | socat - unix-connect:"$UZBL_SOCKET")
-case $result in
- *XXXEMIT_FORM_ACTIVEXXX*)
+case "$1" in
+ XXXEMIT_FORM_ACTIVEXXX)
# a form element was selected
- echo 'event FORM_ACTIVE' > "$UZBL_FIFO" ;;
- *XXXRESET_MODEXXX*)
+ printf 'event FORM_ACTIVE\nevent KEYCMD_CLEAR\n' > "$UZBL_FIFO" ;;
+ XXXRESET_MODEXXX)
# a link was selected, reset uzbl's input mode
printf 'set mode=\nevent KEYCMD_CLEAR\n' > "$UZBL_FIFO" ;;
esac
diff --git a/examples/data/scripts/formfiller.js b/examples/data/scripts/formfiller.js
new file mode 100644
index 0000000..abf0162
--- /dev/null
+++ b/examples/data/scripts/formfiller.js
@@ -0,0 +1,67 @@
+uzbl.formfiller = {
+
+ dump: function() {
+ var rv = '';
+ var allFrames = new Array(window);
+ for ( f=0; f<window.frames.length; ++f ) {
+ allFrames.push(window.frames[f]);
+ }
+ for ( j=0; j<allFrames.length; ++j ) {
+ try {
+ var xp_res = allFrames[j].document.evaluate(
+ '//input', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null
+ );
+ var input;
+ while ( input = xp_res.iterateNext() ) {
+ var type = (input.type?input.type:text);
+ if ( type == 'text' || type == 'password' || type == 'search' ) {
+ rv += '%' + escape(input.name) + '(' + type + '):' + input.value + '\n';
+ }
+ else if ( type == 'checkbox' || type == 'radio' ) {
+ rv += '%' + escape(input.name) + '(' + type + '){' + escape(input.value) + '}:' + (input.checked?'1':'0') + '\n';
+ }
+ }
+ xp_res = allFrames[j].document.evaluate(
+ '//textarea', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null
+ );
+ var input;
+ while ( input = xp_res.iterateNext() ) {
+ rv += '%' + escape(input.name) + '(textarea):\n' + input.value.replace(/\n%/g,"\n\\%") + '\n%\n';
+ }
+ }
+ catch (err) { }
+ }
+ return 'formfillerstart\n' + rv + '%!end';
+ }
+
+ ,
+
+ insert: function(fname, ftype, fvalue, fchecked) {
+ fname = unescape(fname);
+ var allFrames = new Array(window);
+ for ( f=0; f<window.frames.length; ++f ) {
+ allFrames.push(window.frames[f]);
+ }
+ for ( j=0; j<allFrames.length; ++j ) {
+ try {
+ if ( ftype == 'text' || ftype == 'password' || ftype == 'search' || ftype == 'textarea' ) {
+ allFrames[j].document.getElementsByName(fname)[0].value = fvalue;
+ }
+ else if ( ftype == 'checkbox' ) {
+ allFrames[j].document.getElementsByName(fname)[0].checked = fchecked;
+ }
+ else if ( ftype == 'radio' ) {
+ fvalue = unescape(fvalue);
+ var radios = allFrames[j].document.getElementsByName(fname);
+ for ( r=0; r<radios.length; ++r ) {
+ if ( radios[r].value == fvalue ) {
+ radios[r].checked = fchecked;
+ }
+ }
+ }
+ }
+ catch (err) { }
+ }
+ }
+
+}
diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh
index c6822e6..3dc9dc4 100755
--- a/examples/data/scripts/formfiller.sh
+++ b/examples/data/scripts/formfiller.sh
@@ -1,202 +1,146 @@
#!/bin/sh
#
-# Enhanced html form (eg for logins) filler (and manager) for uzbl.
-#
-# uses settings files like: $UZBL_FORMS_DIR/<domain>
-# files contain lines like: !profile=<profile_name>
-# <fieldname>(fieldtype): <value>
-# profile_name should be replaced with a name that will tell sth about that
-# profile
-# fieldtype can be checkbox, text or password, textarea - only for information
-# pupropse (auto-generated) - don't change that
-#
-# Texteares: for textareas edited text can be now splitted into more lines.
-# If there will be text, that doesn't match key line:
-# <fieldname>(fieldtype):<value>
-# then it will be considered as a multiline for the first field above it
-# Keep in mind, that if you make more than one line for fileds like input
-# text fields, then all lines will be inserted into as one line
-#
-# Checkboxes/radio-buttons: to uncheck it type on of the following after the
-# colon:
-# no
-# off
-# 0
-# unchecked
-# false
-# or leave it blank, even without spaces
-# otherwise it will be considered as checked
-#
-# user arg 1:
-# edit: force editing the file (falls back to new if not found)
-# new: start with a new file.
-# load: try to load from file into form
-# add: try to add another profile to an existing file
-# once: edit form using external editor
-#
-# something else (or empty): if file not available: new, otherwise load.
+# action
+# new: add new profile template (creates file if not found), then edit
+# edit: edit file (fall back to 'new' if file not found)
+# load: load from file
+# once: use temporary file to edit form once
+# (empty): if file not available, new; otherwise, load
#
-DMENU_ARGS="-i"
-DMENU_SCHEMA="formfiller"
-DMENU_LINES="3"
-DMENU_PROMPT="Choose profile"
-DMENU_OPTIONS="vertical resize"
+action=$1
-. $UZBL_UTIL_DIR/dmenu.sh
-. $UZBL_UTIL_DIR/editor.sh
-. $UZBL_UTIL_DIR/uzbl-dir.sh
+. "$UZBL_UTIL_DIR/uzbl-dir.sh"
+. "$UZBL_UTIL_DIR/editor.sh"
-RAND=$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -c 1-5)
-MODELINE="> vim:ft=formfiller"
+mkdir -p "$UZBL_FORMS_DIR" || exit
-[ -d "$(dirname $UZBL_FORMS_DIR)" ] || exit 1
-[ -d $UZBL_FORMS_DIR ] || mkdir $UZBL_FORMS_DIR || exit 1
+domain=${UZBL_URI#*://}
+domain=${domain%%/*}
-action=$1
+test "$domain" || exit
-domain=$(echo $UZBL_URI | sed 's/\(http\|https\):\/\/\([^\/]\+\)\/.*/\2/')
-
-if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' -a "$action" != 'add' -a "$action" != 'once' ]; then
- action="new"
- [ -e "$UZBL_FORMS_DIR/$domain" ] && action="load"
-elif [ "$action" = 'edit' ] && [ ! -e "$UZBL_FORMS_DIR/$domain" ]; then
- action="new"
-fi
-
-dumpFunction="function dump() { \
- var rv=''; \
- var allFrames = new Array(window); \
- for(f=0;f<window.frames.length;f=f+1) { \
- allFrames.push(window.frames[f]); \
- } \
- for(j=0;j<allFrames.length;j=j+1) { \
- try { \
- var xp_res=allFrames[j].document.evaluate('//input', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); \
- var input; \
- while(input=xp_res.iterateNext()) { \
- var type=(input.type?input.type:text); \
- if(type == 'text' || type == 'password' || type == 'search') { \
- rv += input.name + '(' + type + '):' + input.value + '\\\\n'; \
- } \
- else if(type == 'checkbox' || type == 'radio') { \
- rv += input.name + '{' + input.value + '}(' + type + '):' + (input.checked?'ON':'OFF') + '\\\\n'; \
- } \
- } \
- xp_res=allFrames[j].document.evaluate('//textarea', allFrames[j].document.documentElement, null, XPathResult.ANY_TYPE,null); \
- var input; \
- while(input=xp_res.iterateNext()) { \
- rv += input.name + '(textarea):' + input.value + '\\\\n'; \
- } \
- } \
- catch(err) { } \
- } \
- return rv; \
-}; "
-
-insertFunction="function insert(fname, ftype, fvalue, fchecked) { \
- var allFrames = new Array(window); \
- for(f=0;f<window.frames.length;f=f+1) { \
- allFrames.push(window.frames[f]); \
- } \
- for(j=0;j<allFrames.length;j=j+1) { \
- try { \
- if(ftype == 'text' || ftype == 'password' || ftype == 'search' || ftype == 'textarea') { \
- allFrames[j].document.getElementsByName(fname)[0].value = fvalue; \
- } \
- else if(ftype == 'checkbox') { \
- allFrames[j].document.getElementsByName(fname)[0].checked = fchecked;\
- } \
- else if(ftype == 'radio') { \
- var radios = allFrames[j].document.getElementsByName(fname); \
- for(r=0;r<radios.length;r+=1) { \
- if(radios[r].value == fvalue) { \
- radios[r].checked = fchecked; \
- } \
- } \
- } \
- } \
- catch(err) { } \
- } \
-}; "
-
-if [ "$action" = 'load' ]; then
- [ -e $UZBL_FORMS_DIR/$domain ] || exit 2
- if [ $(cat $UZBL_FORMS_DIR/$domain | grep "!profile" | wc -l) -gt 1 ]; then
- menu=$(cat $UZBL_FORMS_DIR/$domain | \
- sed -n 's/^!profile=\([^[:blank:]]\+\)/\1/p')
- option=$(printf "$menu" | $DMENU)
- fi
+file=$UZBL_FORMS_DIR/$domain
+
+GenForm ()
+{
+ echo 'js uzbl.formfiller.dump();' \
+ | socat - unix-connect:"$UZBL_SOCKET" \
+ | awk '
+ /^formfillerstart$/ {
+ while (getline) {
+ if ( /^%!end/ ) exit
+ print
+ }
+ }
+ '
+}
+
+GetOption ()
+{
+ DMENU_SCHEME=formfiller
+ DMENU_PROMPT="choose profile"
+ DMENU_LINES=4
- # Remove comments
- sed '/^>/d' -i $tmpfile
-
- sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $UZBL_FORMS_DIR/$domain
- fields=$(cat $UZBL_FORMS_DIR/$domain | \
- sed -n "/^!profile=${option}/,/^!profile=/p" | \
- sed '/^!profile=/d' | \
- sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \
- sed 's/^\(.\+\)$/<{br}>\1/' | \
- tr -d '\n' | \
- sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g')
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \
- sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \
- sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET
-elif [ "$action" = "once" ]; then
- tmpfile=$(mktemp)
- printf 'js %s dump(); \n' "$dumpFunction" | \
- socat - unix-connect:$UZBL_SOCKET | \
- sed -n '/^[^(]\+([^)]\+):/p' > $tmpfile
- echo "$MODELINE" >> $tmpfile
- $UZBL_EDITOR $tmpfile
-
- [ -e $tmpfile ] || exit 2
-
- # Remove comments
- sed '/^>/d' -i $tmpfile
-
- sed 's/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):\(off\|no\|false\|unchecked\|0\|$\)/\1{\2}(\3):0/I;s/^\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[^0]\+/\1{\2}(\3):1/I' -i $tmpfile
- fields=$(cat $tmpfile | \
- sed 's/^\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):/%{>\1\2):<}%/' | \
- sed 's/^\(.\+\)$/<{br}>\1/' | \
- tr -d '\n' | \
- sed 's/<{br}>%{>\([^(]\+(\)\(radio\|checkbox\|text\|search\|textarea\|password\)):<}%/\\n\1\2):/g')
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^(]\+\)(\(password\|text\|search\|textarea\)\+):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\2', '\3', 0);/p" | \
- sed -e 's/@/\\@/g;s/<{br}>/\\\\n/g' | socat - unix-connect:$UZBL_SOCKET
- printf '%s\n' "${fields}" | \
- sed -n -e "s/\([^{]\+\){\([^}]*\)}(\(radio\|checkbox\)):[ ]*\(.\+\)/js $insertFunction; insert('\1', '\3', '\2', \4);/p" | \
- sed -e 's/@/\\@/g' | socat - unix-connect:$UZBL_SOCKET
- rm -f $tmpfile
-else
- if [ "$action" = 'new' -o "$action" = 'add' ]; then
- [ "$action" = 'new' ] && echo "$MODELINE" > $UZBL_FORMS_DIR/$domain
- echo "!profile=NAME_THIS_PROFILE$RAND" >> $UZBL_FORMS_DIR/$domain
- #
- # 2. and 3. line (tr -d and sed) are because, on gmail login for example,
- # <input > tag is splited into lines
- # ex:
- # <input name="Email"
- # type="text"
- # value="">
- # So, tr removes all new lines, and sed inserts new line after each >
- # Next sed selects only <input> tags and only with type = "text" or = "password"
- # If type is first and name is second, then another sed will change their order
- # so the last sed will make output
- # text_from_the_name_attr(text or password):
- #
- # login(text):
- # passwd(password):
- #
- printf 'js %s dump(); \n' "$dumpFunction" | \
- socat - unix-connect:$UZBL_SOCKET | \
- sed -n '/^[^(]\+([^)]\+):/p' >> $UZBL_FORMS_DIR/$domain
+ . "$UZBL_UTIL_DIR/dmenu.sh"
+
+ if [ $(grep -c '^!profile' "$1") -gt 1 ]
+ then sed -n 's/^!profile=//p' "$1" | $DMENU
+ else sed -n 's/^!profile=//p' "$1"
+ fi
+}
+
+ParseProfile ()
+{
+ sed "/^>/d; /^!profile=$1$/,/^!/!d; /^!/d"
+}
+
+ParseFields ()
+{
+ awk '/^%/ {
+
+ sub ( /%/, "" )
+
+ split( $0, parts, /\(|\)|\{|\}/ )
+
+ field = $0
+ sub ( /[^:]*:/, "", field )
+
+ if ( parts[2] ~ /(text|password|search)/ )
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",0);\n",
+ parts[1], parts[2], field )
+
+ else if ( parts[2] ~ /(checkbox|radio)/ )
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",%s);\n",
+ parts[1], parts[2], parts[3], field )
+
+ else if ( parts[2] == "textarea" ) {
+ field = ""
+ while (getline) {
+ if ( /^%/ ) break
+ sub ( /^\\/, "" )
+ gsub ( /"/, "\\\"" )
+ gsub ( /\\/, "\\\\" )
+ field = field $0 "\\n"
+ }
+ printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",0);\n",
+ parts[1], parts[2], field )
+ }
+
+ }'
+}
+
+New ()
+{
+ { echo '!profile=NAME_THIS_PROFILE'
+ GenForm | sed 's/^!/\\!/'
+ echo '!'
+ } >> "$file"
+ chmod 600 "$file"
+ $UZBL_EDITOR "$file"
+}
+
+Edit ()
+ if [ -e "$file" ]
+ then $UZBL_EDITOR "$file"
+ else New
fi
- [ -e "$UZBL_FORMS_DIR/$domain" ] || exit 3 #this should never happen, but you never know.
- $UZBL_EDITOR "$UZBL_FORMS_DIR/$domain" #TODO: if user aborts save in editor, the file is already overwritten
-fi
-# vim:fileencoding=utf-8:sw=4
+Load ()
+{
+ test -e "$file" || exit
+
+ option=$(GetOption "$file")
+
+ case $option in *[!a-zA-Z0-9_-]*) exit 1; esac
+
+ ParseProfile $option < "$file" \
+ | ParseFields \
+ | sed 's/@/\\@/' \
+ > "$UZBL_FIFO"
+}
+
+Once ()
+{
+ tmpfile=/tmp/${0##*/}-$$-tmpfile
+ trap 'rm -f "$tmpfile"' EXIT
+
+ GenForm > "$tmpfile"
+ chmod 600 "$tmpfile"
+
+ $UZBL_EDITOR "$tmpfile"
+
+ test -e "$tmpfile" &&
+ ParseFields < "$tmpfile" \
+ | sed 's/@/\\@' \
+ > "$UZBL_FIFO"
+}
+
+case $action in
+ new) New; Load ;;
+ edit) Edit; Load ;;
+ load) Load ;;
+ once) Once ;;
+ '') if [ -e "$file" ]; then Load; else New; Load; fi ;;
+ *) exit 1
+esac
diff --git a/examples/data/style.css b/examples/data/style.css
index f9b111e..ff055d1 100644
--- a/examples/data/style.css
+++ b/examples/data/style.css
@@ -1,25 +1,25 @@
-.uzbl_highlight { background-color: yellow;}
-.uzbl_h_first { background-color: lightgreen;}
+#uzbl_link_hints > span {
+ z-index: 1000 !important;
-.uzbl_follow { border-style: dotted;
- border-width: thin;
+ background-color: #aaff00 !important;
+ border: 2px solid #556600 !important;
+ margin: 0 !important;
+ padding: 1px !important;
+
+ color: black !important;
+ font-size: 9px !important;
+ line-height: 9px !important;
+ font-weight: bold !important;
+ font-variant: normal !important;
+ text-decoration: none !important;
+
+ -webkit-transform: translate(-5px,-5px);
+ /* opacity: 0.7; */
}
-#uzbl_hint > div {
- display: inline;
- border: 2px solid #4a6600;
- background-color: #b9ff00;
- color: black;
- font-size: 9px;
- font-weight: bold;
- line-height: 9px;
- margin: 0px;
- padding: 0px;
- position: absolute;
- z-index: 1000;
- -webkit-border-radius: 6px;
- text-decoration: none;
- -wekit-transform: scale(1) rotate(0deg) translate(-6px,-5px);
+/* we can have different colours for different types of hints! */
+#uzbl_link_hints.new-window > span {
+ background-color: #ffff00 !important;
}
/* vim:set et ts=4: */
diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim
index bf7108c..1a4172b 100644
--- a/extras/vim/syntax/uzbl.vim
+++ b/extras/vim/syntax/uzbl.vim
@@ -1,7 +1,7 @@
" Vim syntax file
" Language: Uzbl config syntax
-" Maintainer: Mason Larobina <mason.larobina@gmail.com>
-" Contributors: Gregor Uhlenheuer (kongo2002)
+" Maintainer: Gregor Uhlenheuer (kongo2002) <kongo2002@gmail.com>
+" Contributors: Mason Larobina <mason.larobina@gmail.com>
" Pawel Tomak (grodzik) <pawel.tomak@gmail.com>
" Version: 0.1
"
@@ -25,9 +25,6 @@ elseif exists("b:current_syntax")
finish
endif
-" Don't match keywords inside strings
-setl iskeyword=!-~,192-255
-
syn keyword uzblKeyword back forward scroll reload reload_ign_cache stop
syn keyword uzblKeyword zoom_in zoom_out toggle_zoom_type uri script
syn keyword uzblKeyword toggle_status spawn sync_spawn sync_sh sync_spawn_exec
@@ -37,7 +34,11 @@ syn keyword uzblKeyword request menu_add menu_link_add menu_image_add
syn keyword uzblKeyword menu_editable_add menu_separator menu_link_separator
syn keyword uzblKeyword menu_image_separator menu_editable_separator
syn keyword uzblKeyword menu_remove menu_link_remove menu_image_remove
-syn keyword uzblKeyword menu_editable_remove hardcopy include js sh
+syn keyword uzblKeyword menu_editable_remove hardcopy include
+
+" Match 'js' and 'sh' only without a dot in front
+syn match uzblKeyword /\.\@<!sh\s\+/
+syn match uzblKeyword /\.\@<!js\s\+/
" Comments
syn match uzblTodo /TODO:/ contained
diff --git a/src/callbacks.c b/src/callbacks.c
index a40057c..deda426 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -291,6 +291,13 @@ cmd_caret_browsing() {
}
void
+set_current_encoding() {
+ webkit_web_view_set_custom_encoding(uzbl.gui.web_view,
+ uzbl.behave.current_encoding);
+}
+
+
+void
cmd_fifo_dir() {
uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
}
@@ -448,29 +455,45 @@ void
load_status_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
(void) param_spec;
- WebKitWebFrame *frame = webkit_web_view_get_main_frame(web_view);
+ WebKitWebFrame *frame;
WebKitLoadStatus status = webkit_web_view_get_load_status(web_view);
switch(status) {
case WEBKIT_LOAD_PROVISIONAL:
- send_event(LOAD_START, NULL, TYPE_STR, uzbl.state.uri ? uzbl.state.uri : "", NULL);
+ send_event(LOAD_START, NULL, TYPE_STR, uzbl.state.uri ? uzbl.state.uri : "", NULL);
break;
case WEBKIT_LOAD_COMMITTED:
- g_free (uzbl.state.uri);
- GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
- uzbl.state.uri = g_string_free (newuri, FALSE);
- g_setenv("UZBL_URI", uzbl.state.uri, TRUE);
-
+ frame = webkit_web_view_get_main_frame(web_view);
send_event(LOAD_COMMIT, NULL, TYPE_STR, webkit_web_frame_get_uri (frame), NULL);
break;
case WEBKIT_LOAD_FINISHED:
- send_event(LOAD_FINISH, NULL, TYPE_STR, webkit_web_frame_get_uri(frame), NULL);
+ send_event(LOAD_FINISH, NULL, TYPE_STR, uzbl.state.uri, NULL);
break;
case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
break; /* we don't do anything with this (yet) */
case WEBKIT_LOAD_FAILED:
break; /* load_error_cb will handle this case */
}
+}
+void
+load_error_cb (WebKitWebView* page, WebKitWebFrame* frame, gchar *uri, gpointer web_err, gpointer ud) {
+ (void) page; (void) frame; (void) ud;
+ GError *err = web_err;
+
+ send_event (LOAD_ERROR, NULL,
+ TYPE_STR, uri,
+ TYPE_INT, err->code,
+ TYPE_STR, err->message,
+ NULL);
+}
+
+void
+uri_change_cb (WebKitWebView *web_view, GParamSpec param_spec) {
+ (void) param_spec;
+
+ g_free (uzbl.state.uri);
+ g_object_get (web_view, "uri", &uzbl.state.uri, NULL);
+ g_setenv("UZBL_URI", uzbl.state.uri, TRUE);
}
void
@@ -485,20 +508,6 @@ selection_changed_cb(WebKitWebView *webkitwebview, gpointer ud) {
}
void
-load_error_cb (WebKitWebView* page, WebKitWebFrame* frame, gchar *uri, gpointer web_err, gpointer ud) {
- (void) page;
- (void) frame;
- (void) ud;
- GError *err = web_err;
-
- send_event (LOAD_ERROR, NULL,
- TYPE_STR, uri,
- TYPE_INT, err->code,
- TYPE_STR, err->message,
- NULL);
-}
-
-void
destroy_cb (GtkWidget* widget, gpointer data) {
(void) widget;
(void) data;
@@ -957,16 +966,28 @@ scroll_horiz_cb(GtkAdjustment *adjust, void *w)
}
void
-run_menu_command(GtkWidget *menu, const char *line) {
+run_menu_command(GtkWidget *menu, MenuItem *mi) {
(void) menu;
- parse_cmd_line(line, NULL);
+ if (mi->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ gchar* uri;
+ g_object_get(mi->hittest, "image-uri", &uri, NULL);
+ gchar* cmd = g_strdup_printf("%s %s", mi->cmd, uri);
+
+ parse_cmd_line(cmd, NULL);
+
+ g_free(cmd);
+ g_free(uri);
+ g_object_unref(mi->hittest);
+ }
+ else {
+ parse_cmd_line(mi->cmd, NULL);
+ }
}
void
populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
- (void) v;
(void) c;
GUI *g = &uzbl.gui;
GtkWidget *item;
@@ -981,11 +1002,19 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
if((context = get_click_context(NULL)) == -1)
return;
-
for(i=0; i < uzbl.gui.menu_items->len; i++) {
hit = 0;
mi = g_ptr_array_index(uzbl.gui.menu_items, i);
+ if (mi->context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ GdkEventButton ev;
+ gint x, y;
+ gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(v)), &x, &y, NULL);
+ ev.x = x;
+ ev.y = y;
+ mi->hittest = webkit_web_view_get_hit_test_result(v, &ev);
+ }
+
if((mi->context > WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) &&
(context & mi->context)) {
if(mi->issep) {
@@ -996,7 +1025,7 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
- G_CALLBACK(run_menu_command), mi->cmd);
+ G_CALLBACK(run_menu_command), mi);
gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
@@ -1014,7 +1043,7 @@ populate_popup_cb(WebKitWebView *v, GtkMenu *m, void *c) {
else {
item = gtk_menu_item_new_with_label(mi->name);
g_signal_connect(item, "activate",
- G_CALLBACK(run_menu_command), mi->cmd);
+ G_CALLBACK(run_menu_command), mi);
gtk_menu_shell_append(GTK_MENU_SHELL(m), item);
gtk_widget_show(item);
}
diff --git a/src/callbacks.h b/src/callbacks.h
index 13cb83d..d34b9fa 100644
--- a/src/callbacks.h
+++ b/src/callbacks.h
@@ -110,6 +110,9 @@ void
cmd_default_encoding();
void
+set_current_encoding();
+
+void
cmd_enforce_96dpi();
void
@@ -158,6 +161,9 @@ void
load_error_cb (WebKitWebView* page, WebKitWebFrame* frame, gchar *uri, gpointer web_err, gpointer ud);
void
+uri_change_cb (WebKitWebView *web_view, GParamSpec param_spec);
+
+void
selection_changed_cb(WebKitWebView *webkitwebview, gpointer ud);
void
diff --git a/src/events.c b/src/events.c
index 174ff75..58dddfc 100644
--- a/src/events.c
+++ b/src/events.c
@@ -92,7 +92,7 @@ send_event_sockets(GPtrArray *sockets, GString *msg) {
}
}
-static void
+void
replay_buffered_events() {
guint i = 0;
@@ -205,6 +205,7 @@ void
key_to_event(guint keyval, gint mode) {
gchar ucs[7];
gint ulen;
+ gchar *keyname;
guint32 ukval = gdk_keyval_to_unicode(keyval);
/* check for printable unicode char */
@@ -219,9 +220,9 @@ key_to_event(guint keyval, gint mode) {
NULL, TYPE_FORMATTEDSTR, ucs, NULL);
}
/* send keysym for non-printable chars */
- else {
+ else if((keyname = gdk_keyval_name(keyval))){
send_event(mode == GDK_KEY_PRESS ? KEY_PRESS : KEY_RELEASE,
- NULL, TYPE_NAME, gdk_keyval_name(keyval), NULL);
+ NULL, TYPE_NAME, keyname , NULL);
}
}
diff --git a/src/events.h b/src/events.h
index 8e75097..bd519a6 100644
--- a/src/events.h
+++ b/src/events.h
@@ -33,10 +33,7 @@ void
event_buffer_timeout(guint sec);
void
-send_event_socket(GString *msg);
-
-void
-send_event_stdout(GString *msg);
+replay_buffered_events();
void
vsend_event(int type, const gchar *custom_event, va_list vargs);
diff --git a/src/io.c b/src/io.c
index d9a2488..3f8fa2f 100644
--- a/src/io.c
+++ b/src/io.c
@@ -123,9 +123,14 @@ control_stdin(GIOChannel *gio, GIOCondition condition) {
if ( (ret == G_IO_STATUS_ERROR) || (ret == G_IO_STATUS_EOF) )
return FALSE;
- parse_cmd_line(ctl_line, NULL);
+ GString *result = g_string_new("");
+
+ parse_cmd_line(ctl_line, result);
g_free(ctl_line);
+ puts(result->str);
+ g_string_free(result, TRUE);
+
return TRUE;
}
@@ -211,8 +216,8 @@ init_connect_socket() {
}
/* replay buffered events */
- if(replay)
- send_event_socket(NULL);
+ if(replay && uzbl.state.event_buffer)
+ replay_buffered_events();
}
diff --git a/src/util.c b/src/util.c
index 345bf2f..8f6c349 100644
--- a/src/util.c
+++ b/src/util.c
@@ -6,6 +6,8 @@
#include "util.h"
+gchar* find_existing_file2(gchar *, const gchar *);
+
const XDG_Var XDG[] = {
{ "XDG_CONFIG_HOME", "~/.config" },
{ "XDG_DATA_HOME", "~/.local/share" },
@@ -16,56 +18,41 @@ const XDG_Var XDG[] = {
/*@null@*/ gchar*
get_xdg_var (XDG_Var xdg) {
- const gchar* actual_value = getenv (xdg.environmental);
- const gchar* home = getenv ("HOME");
- gchar* return_value;
-
- if (! actual_value || strcmp (actual_value, "") == 0) {
- if (xdg.default_value) {
- return_value = str_replace ("~", home, xdg.default_value);
- } else {
- return_value = NULL;
- }
- } else {
- return_value = str_replace("~", home, actual_value);
- }
+ const gchar *actual_value = getenv(xdg.environmental);
+ const gchar *home = getenv("HOME");
+
+ if (!actual_value || !actual_value[0])
+ actual_value = xdg.default_value;
- return return_value;
+ if (!actual_value)
+ return NULL;
+
+ return str_replace("~", home, actual_value);
}
/*@null@*/ gchar*
-find_xdg_file (int xdg_type, const char* filename) {
+find_xdg_file (int xdg_type, const char* basename) {
/* xdg_type = 0 => config
xdg_type = 1 => data
- xdg_type = 2 => cache*/
+ xdg_type = 2 => cache */
- gchar* xdgv = get_xdg_var (XDG[xdg_type]);
- gchar* temporary_file = g_strconcat (xdgv, filename, NULL);
+ gchar *xdgv = get_xdg_var(XDG[xdg_type]);
+ gchar *path = g_strconcat (xdgv, basename, NULL);
g_free (xdgv);
- gchar* temporary_string;
- char* saveptr;
- char* buf;
-
- if (! file_exists (temporary_file) && xdg_type != 2) {
- buf = get_xdg_var (XDG[3 + xdg_type]);
- temporary_string = (char *) strtok_r (buf, ":", &saveptr);
- g_free(buf);
+ if (file_exists(path))
+ return path;
- while ((temporary_string = (char * ) strtok_r (NULL, ":", &saveptr)) && ! file_exists (temporary_file)) {
- g_free (temporary_file);
- temporary_file = g_strconcat (temporary_string, filename, NULL);
- }
- }
+ if (xdg_type == 2)
+ return NULL;
- //g_free (temporary_string); - segfaults.
+ /* the file doesn't exist in the expected directory.
+ * check if it exists in one of the system-wide directories. */
+ char *system_dirs = get_xdg_var(XDG[3 + xdg_type]);
+ path = find_existing_file2(system_dirs, basename);
+ g_free(system_dirs);
- if (file_exists (temporary_file)) {
- return temporary_file;
- } else {
- g_free(temporary_file);
- return NULL;
- }
+ return path;
}
gboolean
@@ -95,85 +82,66 @@ for_each_line_in_file(const gchar *path, void (*callback)(const gchar *l, void *
GIOChannel *chan = g_io_channel_new_file(path, "r", NULL);
- if (chan) {
- while (g_io_channel_read_line(chan, &line, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
- callback(line, user_data);
- g_free(line);
- }
- g_io_channel_unref (chan);
+ if (!chan)
+ return FALSE;
- return TRUE;
+ while (g_io_channel_read_line(chan, &line, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
+ callback(line, user_data);
+ g_free(line);
}
- return FALSE;
-}
+ g_io_channel_unref (chan);
-enum exp_type
-get_exp_type(const gchar *s) {
- /* variables */
- if(*(s+1) == '(')
- return EXP_EXPR;
- else if(*(s+1) == '{')
- return EXP_BRACED_VAR;
- else if(*(s+1) == '<')
- return EXP_JS;
- else if(*(s+1) == '[')
- return EXP_ESCAPE;
- else
- return EXP_SIMPLE_VAR;
-
- /*@notreached@*/
-return EXP_ERR;
+ return TRUE;
}
-
-/* search a PATH style string for an existing file+path combination */
+/* This function searches the directories in the : separated ($PATH style)
+ * string 'dirs' for a file named 'basename'. It returns the path of the first
+ * file found, or NULL if none could be found.
+ * NOTE: this function modifies the 'dirs' argument. */
gchar*
-find_existing_file(gchar* path_list) {
- int i=0;
- int cnt;
- gchar **split;
- gchar *tmp = NULL;
- gchar *executable;
+find_existing_file2(gchar *dirs, const gchar *basename) {
+ char *saveptr = NULL;
+
+ /* iterate through the : separated elements until we find our file. */
+ char *tok = strtok_r(dirs, ":", &saveptr);
+ char *path = g_strconcat (tok, "/", basename, NULL);
+
+ while (!file_exists(path)) {
+ g_free(path);
+
+ tok = strtok_r(NULL, ":", &saveptr);
+ if (!tok)
+ return NULL; /* we've hit the end of the string */
+ path = g_strconcat (tok, "/", basename, NULL);
+ }
+
+ return path;
+}
+
+/* search a PATH style string for an existing file+path combination.
+ * everything after the last ':' is assumed to be the name of the file.
+ * e.g. "/tmp:/home:a/file" will look for /tmp/a/file and /home/a/file.
+ *
+ * if there are no :s then the entire thing is taken to be the path. */
+gchar*
+find_existing_file(const gchar* path_list) {
if(!path_list)
return NULL;
- split = g_strsplit(path_list, ":", 0);
- while(split[i])
- i++;
+ char *path_list_dup = g_strdup(path_list);
- if(i<=1) {
- tmp = g_strdup(split[0]);
- g_strfreev(split);
- return tmp;
- }
- else
- cnt = i-1;
-
- i=0;
- tmp = g_strdup(split[cnt]);
- g_strstrip(tmp);
- if(tmp[0] == '/')
- executable = g_strdup_printf("%s", tmp+1);
- else
- executable = g_strdup(tmp);
- g_free(tmp);
-
- while(i<cnt) {
- tmp = g_strconcat(g_strstrip(split[i]), "/", executable, NULL);
- if(g_file_test(tmp, G_FILE_TEST_EXISTS)) {
- g_strfreev(split);
- return tmp;
- }
- else
- g_free(tmp);
- i++;
- }
+ char *basename = strrchr(path_list_dup, ':');
+ if(!basename)
+ return path_list_dup;
+
+ basename[0] = '\0';
+ basename++;
- g_free(executable);
- g_strfreev(split);
- return NULL;
+ char *result = find_existing_file2(path_list_dup, basename);
+ g_free(path_list_dup);
+ return result;
}
gchar*
diff --git a/src/util.h b/src/util.h
index db19930..75a614b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -6,16 +6,12 @@ typedef struct {
gchar* default_value;
} XDG_Var;
-enum exp_type {EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE};
-
-
gchar* get_xdg_var(XDG_Var xdg);
gchar* find_xdg_file(int xdg_type, const char* filename);
gboolean file_exists(const char* filename);
char* str_replace(const char* search, const char* replace, const char* string);
gboolean for_each_line_in_file(const gchar *path, void (*callback)(const gchar *l, void *c), void *user_data);
-enum exp_type get_exp_type(const gchar*);
-gchar* find_existing_file(gchar*);
+gchar* find_existing_file(const gchar*);
gchar* argv_idx(const GArray*, const guint);
/**
* appends `src' to `dest' with backslash, single-quotes and newlines in
diff --git a/src/uzbl-core.c b/src/uzbl-core.c
index 26b3dba..d73fbdf 100644
--- a/src/uzbl-core.c
+++ b/src/uzbl-core.c
@@ -131,6 +131,7 @@ const struct var_name_to_ptr_t {
{ "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)},
{ "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)},
{ "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)},
+ { "current_encoding", PTR_V_STR(uzbl.behave.current_encoding, 1, set_current_encoding)},
{ "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)},
{ "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)},
{ "scrollbars_visible", PTR_V_INT(uzbl.gui.scrollbars_visible, 1, cmd_scrollbars_visibility)},
@@ -165,6 +166,20 @@ create_var_to_name_hash() {
/* --- UTILITY FUNCTIONS --- */
+enum exp_type {
+ EXP_ERR, EXP_SIMPLE_VAR, EXP_BRACED_VAR, EXP_EXPR, EXP_JS, EXP_ESCAPE
+};
+
+enum exp_type
+get_exp_type(const gchar *s) {
+ switch(*(s+1)) {
+ case '(': return EXP_EXPR;
+ case '{': return EXP_BRACED_VAR;
+ case '<': return EXP_JS;
+ case '[': return EXP_ESCAPE;
+ default: return EXP_SIMPLE_VAR;
+ }
+}
/*
* recurse == 1: don't expand '@(command)@'
@@ -608,6 +623,9 @@ print(WebKitWebView *page, GArray *argv, GString *result) {
(void) page; (void) result;
gchar* buf;
+ if(!result)
+ return;
+
buf = expand(argv_idx(argv, 0), 0);
g_string_assign(result, buf);
g_free(buf);
@@ -720,7 +738,8 @@ act_dump_config_as_events() {
void
load_uri(WebKitWebView *web_view, GArray *argv, GString *result) {
(void) web_view; (void) result;
- set_var_value("uri", argv_idx(argv, 0));
+ gchar * uri = argv_idx(argv, 0);
+ set_var_value("uri", uri ? uri : "");
}
/* Javascript*/
@@ -744,7 +763,7 @@ eval_js(WebKitWebView * web_view, gchar *script, GString *result, const char *fi
js_script = JSStringCreateWithUTF8CString(script);
js_file = JSStringCreateWithUTF8CString(file);
js_result = JSEvaluateScript(context, js_script, globalobject, js_file, 0, &js_exc);
- if (js_result && !JSValueIsUndefined(context, js_result)) {
+ if (result && js_result && !JSValueIsUndefined(context, js_result)) {
js_result_string = JSValueToStringCopy(context, js_result, NULL);
js_result_size = JSStringGetMaximumUTF8CStringSize(js_result_string);
@@ -1143,8 +1162,10 @@ parse_command_parts(const gchar *line, GArray *a) {
CommandInfo *c = NULL;
gchar *exp_line = expand(line, 0);
- if(exp_line[0] == '\0')
+ if(exp_line[0] == '\0') {
+ g_free(exp_line);
return NULL;
+ }
/* separate the line into the command and its parameters */
gchar **tokens = g_strsplit(exp_line, " ", 2);
@@ -1212,7 +1233,8 @@ move_statusbar() {
}
g_object_unref(uzbl.gui.scrolled_win);
g_object_unref(uzbl.gui.mainbar);
- gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
+ if (!uzbl.state.plug_mode)
+ gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view));
return;
}
@@ -1258,6 +1280,8 @@ set_var_value(const gchar *name, gchar *val) {
char *endp = NULL;
char *buf = NULL;
+ g_assert(val != NULL);
+
if( (c = g_hash_table_lookup(uzbl.comm.proto_var, name)) ) {
if(!c->writeable) return FALSE;
@@ -1370,7 +1394,6 @@ update_title(void) {
}
}
-
void
create_scrolled_win() {
GUI* g = &uzbl.gui;
@@ -1399,6 +1422,7 @@ create_scrolled_win() {
"signal::selection-changed", (GCallback)selection_changed_cb, NULL,
"signal::notify::progress", (GCallback)progress_change_cb, NULL,
"signal::notify::load-status", (GCallback)load_status_change_cb, NULL,
+ "signal::notify::uri", (GCallback)uri_change_cb, NULL,
"signal::load-error", (GCallback)load_error_cb, NULL,
"signal::hovering-over-link", (GCallback)link_hover_cb, NULL,
"signal::navigation-policy-decision-requested", (GCallback)navigation_decision_cb, NULL,
diff --git a/src/uzbl-core.h b/src/uzbl-core.h
index 3240fc6..766fe56 100644
--- a/src/uzbl-core.h
+++ b/src/uzbl-core.h
@@ -165,7 +165,6 @@ typedef struct {
gchar* cursive_font_family;
gboolean forward_keys;
- guint modmask;
guint http_debug;
gchar* shell_cmd;
guint view_source;
@@ -187,12 +186,11 @@ typedef struct {
gchar* style_uri;
guint resizable_txt;
gchar* default_encoding;
+ gchar* current_encoding;
guint enforce_96dpi;
gchar *inject_html;
guint caret_browsing;
guint javascript_windows;
- guint mode;
- gchar* base_url;
gboolean print_version;
/* command list: (key)name -> (value)Command */
@@ -203,14 +201,6 @@ typedef struct {
} Behaviour;
-/* Javascript */
-typedef struct {
- gboolean initialized;
- JSClassDefinition classdef;
- JSClassRef classref;
-} Javascript;
-
-
/* Static information */
typedef struct {
int webkit_major;
@@ -229,7 +219,6 @@ typedef struct {
Network net;
Behaviour behave;
Communication comm;
- Javascript js;
Info info;
Window xwin;
@@ -362,6 +351,7 @@ typedef struct {
gchar* cmd;
gboolean issep;
guint context;
+ WebKitHitTestResult* hittest;
} MenuItem;
#endif
diff --git a/tests/test-command.c b/tests/test-command.c
index 55bf316..6b55fb3 100644
--- a/tests/test-command.c
+++ b/tests/test-command.c
@@ -282,6 +282,11 @@ test_js (void) {
g_string_free(result, TRUE);
}
+void test_uri(void) {
+ /* Testing for a crash, not crashing is a pass */
+ parse_cmd_line("uri", NULL);
+}
+
void
test_last_result (void) {
GString *result = g_string_new("");
@@ -313,6 +318,7 @@ main (int argc, char *argv[]) {
g_test_add("/test-command/event", struct EventFixture, NULL, event_fixture_setup, test_event, event_fixture_teardown);
g_test_add_func("/test-command/print", test_print);
+ g_test_add_func("/test-command/uri", test_uri);
g_test_add_func("/test-command/scroll", test_scroll);
g_test_add_func("/test-command/toggle-status", test_toggle_status);
g_test_add_func("/test-command/sync-sh", test_sync_sh);
@@ -329,7 +335,7 @@ main (int argc, char *argv[]) {
uzbl.state.config_file = "/tmp/uzbl-config";
uzbl.comm.fifo_path = "/tmp/some-nonexistant-fifo";
uzbl.comm.socket_path = "/tmp/some-nonexistant-socket";
- uzbl.state.uri = "http://example.org/";
+ uzbl.state.uri = g_strdup("http://example.org/");
uzbl.gui.main_title = "Example.Org";
uzbl.state.instance_name = INSTANCE_NAME;