diff options
-rw-r--r-- | api/dwb-js.7 | 47 | ||||
-rw-r--r-- | api/jsapi.7.txt | 17 | ||||
-rw-r--r-- | api/jsapi.txt | 35 | ||||
-rw-r--r-- | doc/dwb.1 | 12 | ||||
-rw-r--r-- | doc/dwb.1.txt | 4 | ||||
-rw-r--r-- | extensions/formfiller | 552 | ||||
-rw-r--r-- | extensions/unique_tabs | 125 | ||||
-rw-r--r-- | extensions/userscripts | 760 | ||||
-rw-r--r-- | scripts/lib/dwb.js | 2 | ||||
-rw-r--r-- | scripts/lib/signals.js | 6 | ||||
-rw-r--r-- | scripts/lib/util.js | 1 | ||||
-rw-r--r-- | src/adblock.c | 3 | ||||
-rw-r--r-- | src/commands.c | 21 | ||||
-rw-r--r-- | src/config.h | 2 | ||||
-rw-r--r-- | src/dom.c | 121 | ||||
-rw-r--r-- | src/dom.h | 26 | ||||
-rw-r--r-- | src/dwb.c | 256 | ||||
-rw-r--r-- | src/dwb.h | 5 | ||||
-rw-r--r-- | src/editor.c | 177 | ||||
-rw-r--r-- | src/editor.h | 24 | ||||
-rw-r--r-- | src/plugins.c | 3 | ||||
-rw-r--r-- | src/scripts.c | 12 | ||||
-rw-r--r-- | src/scripts.h | 1 | ||||
-rw-r--r-- | src/view.c | 5 | ||||
-rw-r--r-- | util/settings.pre | 1 |
25 files changed, 1328 insertions, 890 deletions
diff --git a/api/dwb-js.7 b/api/dwb-js.7 index 8964b910..5e0e6959 100644 --- a/api/dwb-js.7 +++ b/api/dwb-js.7 @@ -2,12 +2,12 @@ .\" Title: dwb-js .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.77.1 <http://docbook.sf.net/> -.\" Date: 01/17/2013 +.\" Date: 01/28/2013 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "DWB\-JS" "7" "01/17/2013" "\ \&" "\ \&" +.TH "DWB\-JS" "7" "01/28/2013" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -90,6 +90,11 @@ for details\&. .RS 4 Refers to the global object .RE +.PP +\fIsession (SoupSession, read)\fR +.RS 4 +The webkit session +.RE .SS "Methods" .sp .it 1 an-trap @@ -457,7 +462,7 @@ Either the function or the optional name passed to bind\&. .PP \fIreturns\fR .RS 4 -true if the function is unbinded +true if the function was unbound .RE .RE .SH "GLOBAL OBJECTS" @@ -2456,6 +2461,42 @@ The Download .nr an-break-flag 1 .br .ps +1 +\fBexecuteCommand\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +signals\&.connect("executeCommand", function(detail)); +.fi +.if n \{\ +.RE +.\} +.sp +Emitted before a command is executed, return true to prevent the execution\&. +.PP +\fIdetail\&.command\fR +.RS 4 +The command that is executed\&. +.RE +.PP +\fIdetail\&.argument\fR +.RS 4 +The command that is executed\&. +.RE +.PP +\fIdetail\&.nummod\fR +.RS 4 +The numerical modifier, will be \-1 if no numerical modifier was used\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 \fBframeCreated\fR .RS 4 .sp diff --git a/api/jsapi.7.txt b/api/jsapi.7.txt index ec361e4d..8a0dce57 100644 --- a/api/jsapi.7.txt +++ b/api/jsapi.7.txt @@ -45,6 +45,7 @@ properties on the global object, see also *Global data* for details. === Properties _global (Object, read)_;; Refers to the global object +_session (SoupSession, read)_;; The webkit session === Methods === @@ -228,7 +229,7 @@ _returns_;; true if the timer was stopped Unbind a shortcut that was previously binded with bind _func_ or _name_;; Either the function or the optional name passed to bind. -_returns_;; true if the function is unbinded +_returns_;; true if the function was unbound **** @@ -413,7 +414,6 @@ _n_;; Number of the tab _returns_;; The corresponding webview **** - === util === The util object implements helper methods. @@ -1223,6 +1223,19 @@ Emitted when the DownloadStatus changes. _download_;; The Download **** +==== executeCommand +**** +---- +signals.connect("executeCommand", function(detail)); +---- + +Emitted before a command is executed, return true to prevent the execution. + +_detail.command_;; The command that is executed. +_detail.argument_;; The command that is executed. +_detail.nummod_;; The numerical modifier, will be -1 if no numerical modifier +was used. +**** ==== frameCreated **** diff --git a/api/jsapi.txt b/api/jsapi.txt index efe43ff3..cefefed5 100644 --- a/api/jsapi.txt +++ b/api/jsapi.txt @@ -52,6 +52,18 @@ global object read Refers to the global object. **** +**** +[float] +==== *session* ==== + +[source,javascript] +---- +session SoupSession read +---- + +The webkit session. +**** + === Methods === @@ -312,7 +324,7 @@ Unbind a shortcut that was previously binded with <<bind>> :: _func_ or _name_;; Either the function or the optional name passed to <<bind>>. -_returns_;; +true+ if the function is unbinded +_returns_;; +true+ if the function was unbound **** ====== @@ -758,7 +770,6 @@ _n_;; Number of the tab _returns_;; The corresponding <<webview>> **** - ==== .Example [source,javascript] @@ -2329,6 +2340,26 @@ Emitted when the <<DownloadStatus>> changes. _download_;; The <<Download>> **** +**** +[[executeCommand]] +[float] +==== *executeCommand* ==== + +[source,javascript] +---- +Boolean callback(detail) +---- + +Emitted before a command is executed, return true to prevent the execution. + + :: + +_detail.command_;; The command that is executed. +_detail.argument_;; The command that is executed. +_detail.nummod_;; The numerical modifier, will be -1 if no numerical modifier +was used. +**** + **** [[frameCreated]] @@ -2,12 +2,12 @@ .\" Title: dwb .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.77.1 <http://docbook.sf.net/> -.\" Date: 01/09/2013 +.\" Date: 01/27/2013 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "DWB" "1" "01/09/2013" "\ \&" "\ \&" +.TH "DWB" "1" "01/27/2013" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -3038,6 +3038,14 @@ The value by which the zoom level is changed when zooming in or out\&. Possible .RE .SS "Other settings" .PP +\fBaccept\-language\fR +.RS 4 +If set it will be used to set +\fIAccept\-Language\fR +header for all requests, default value: +\fINULL\fR +.RE +.PP \fBactive\-completion\-bg\-color\fR .RS 4 The background color for an active element in tab\-completion\&. Possible values: an rgb color\-string, default value: diff --git a/doc/dwb.1.txt b/doc/dwb.1.txt index 23754700..05bb64f9 100644 --- a/doc/dwb.1.txt +++ b/doc/dwb.1.txt @@ -1148,6 +1148,10 @@ default value: Other settings ~~~~~~~~~~~~~~ +*accept-language*:: +If set it will be used to set 'Accept-Language' header for all requests, +default value: +'NULL' *active-completion-bg-color*:: The background color for an active element in tab-completion. Possible values: diff --git a/extensions/formfiller b/extensions/formfiller index acdeb6db..db417628 100644 --- a/extensions/formfiller +++ b/extensions/formfiller @@ -99,29 +99,29 @@ INFO>*/ var me = "formfiller"; var defaultConfig = { //<DEFAULT_CONFIG - // shortcut that gets and saves formdata - scGetForm : "efg", +// shortcut that gets and saves formdata +scGetForm : "efg", - // shortcut that fills a form - scFillForm : "eff", +// shortcut that fills a form +scFillForm : "eff", - // path to the formdata file - formData : data.configDir + "/forms", +// path to the formdata file +formData : data.configDir + "/forms", - // whether to use a gpg-encrypted file - useGPG : false, +// whether to use a gpg-encrypted file +useGPG : false, - // additional arguments passed to gpg2 when encrypting the formdata - GPGOptEncrypt : "", +// additional arguments passed to gpg2 when encrypting the formdata +GPGOptEncrypt : "", - // additional arguments passed to gpg2 when decrypting the formdata - GPGOptDecrypt : "", +// additional arguments passed to gpg2 when decrypting the formdata +GPGOptDecrypt : "", - // whether to save the password in memory when gpg is used - keepPassword : true, - - // whether to save the whole formdata in memory when gpg is used - keepFormdata : false +// whether to save the password in memory when gpg is used +keepPassword : true, + +// whether to save the whole formdata in memory when gpg is used +keepFormdata : false //>DEFAULT_CONFIG }; @@ -129,286 +129,324 @@ var config = {}; var passWord = null; var formData = null; -var injectGetForm = function () {//{{{ - var ret = null; - var forms = document.forms; +var injectGetForm = function () //{{{ +{ + var ret = null; + var forms = document.forms; - function objectifyForm(f) { - var query = "descendant::input[not(@type='hidden') and (@type='text' or @type='password' or @type='checkbox' or not(@type) or @type='email')]"; - var input, data; - var r = document.evaluate(query, f, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); - var o = {}; - o.id = f.id || null; - o.form = {}; - var hasValue = false; - var hasIds = true; + function objectifyForm(f) + { + var query = "descendant::input[not(@type='hidden') and (@type='text' or @type='password' or @type='checkbox' or not(@type) or @type='email')]"; + var input, data; + var r = document.evaluate(query, f, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); + var o = {}; + o.id = f.id || null; + o.form = {}; + var hasValue = false; + var hasIds = true; - while ((input = r.iterateNext()) !== null) { - if (input.value && !(/^\s*$/.test(input.value))) { - if (/^\**$/.test(input.value) ) - return null; - if (!input.type || input.type.toLowerCase() === "text" || input.type.toLowerCase() === "password") - hasValue = true; - data = {}; - if (input.id) - data.id = input.id; - else - hasIds = false; - data.value = input.value; - o.form[input.name] = data; - } - } - if (hasValue) { - var ret = {}; - o.hasIds = hasIds; - ret[window.location.host] = o; - return ret; + while ((input = r.iterateNext()) !== null) + { + if (input.value && !(/^\s*$/.test(input.value))) + { + if (/^\**$/.test(input.value) ) + return null; + if (!input.type || input.type.toLowerCase() === "text" || input.type.toLowerCase() === "password") + hasValue = true; + data = {}; + if (input.id) + data.id = input.id; + else + hasIds = false; + data.value = input.value; + o.form[input.name] = data; + } + } + if (hasValue) + { + var ret = {}; + o.hasIds = hasIds; + ret[window.location.host] = o; + return ret; + } + return null; } - return null; - } - for (var i=0; i<forms.length; i++) { - if ((ret = objectifyForm(forms[i])) !== null) { - return ret; + for (var i=0; i<forms.length; i++) + { + if ((ret = objectifyForm(forms[i])) !== null) + return ret; } - } - return ret; + return ret; };//}}} -var injectFillForm = function () {//{{{ - var key, i, forms, form = null, input; - var data = arguments[0]; +var injectFillForm = function () //{{{ +{ + var key, i, forms, form = null, input; + var data = arguments[0]; - function fillInput(input, key) { - var value = data.form[key].value; - if(input.type=="checkbox" || input.type=="radio") - input.checked=(value.toLowerCase() !== "false" && value !== "0"); - else { - input.value = value; - } - } - function setValues(form) { - var input, value; - for (key in data.form) { - if (!form[key]) - return null; + function fillInput(input, key) + { + var value = data.form[key].value; + if(input.type=="checkbox" || input.type=="radio") + input.checked=(value.toLowerCase() !== "false" && value !== "0"); + else + input.value = value; + } - for (key in data.form) { - fillInput(form[key], key); + function setValues(form) + { + var input, value; + for (key in data.form) + { + if (!form[key]) + return null; + } + for (key in data.form) + { + fillInput(form[key], key); + } + return form; } - return form; - } - function fillElementsById() { - var input; - for (key in data.form) { - input = document.getElementById(data.form[key].id); - if (input === null || input === undefined) { - return null; - } - fillInput(input, key); + function fillElementsById() + { + var input; + for (key in data.form) + { + input = document.getElementById(data.form[key].id); + if (input === null || input === undefined) + return null; + + fillInput(input, key); + } + return input.form || null; } - return input.form || null; - } - function fillFormById() { - var form = document.getElementById(data.id); + function fillFormById() + { + var form = document.getElementById(data.id); + if (form === null) + return null; + return setValues(form); + } + if (data.hasIds) + form = fillElementsById(); + + if (form === null && data.id !== undefined && data.id !== null) + form = fillFormById(); + if (form === null) - return null; - return setValues(form); - } - if (data.hasIds) { - form = fillElementsById(); - } - if (form === null && data.id !== undefined && data.id !== null) { - form = fillFormById(); - } - if (form === null) { - forms = document.forms; - for (i=0; i<forms.length && form === null; i++) { - form = setValues(forms[i]); + { + forms = document.forms; + for (i=0; i<forms.length && form === null; i++) + { + form = setValues(forms[i]); + } } - } - if (form !== null && data.autosubmit) { - var buttons = form.querySelectorAll("[type='submit']"); - for (i=0; i<buttons.length; i++) { - var e = buttons[i]; - var mouseEvent = e.ownerDocument.createEvent("MouseEvent"); - mouseEvent.initMouseEvent("click", false, true, - e.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, - 0, null); - e.dispatchEvent(mouseEvent); + if (form !== null && data.autosubmit) + { + var buttons = form.querySelectorAll("[type='submit']"); + for (i=0; i<buttons.length; i++) + { + var e = buttons[i]; + var mouseEvent = e.ownerDocument.createEvent("MouseEvent"); + mouseEvent.initMouseEvent("click", false, true, + e.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, + 0, null); + e.dispatchEvent(mouseEvent); + } + form.submit(); } - form.submit(); - } - return form !== null; + return form !== null; };//}}} -function getFormData(callback) {//{{{ - var stat, ret; - if (config.useGPG) { - if (formData !== null) { - return formData; - } - getPassWord(); - stat = system.spawnSync("gpg2 " + config.GPGOptDecrypt + " --passphrase " + passWord + " --batch --no-tty --yes -d " + config.formData); - if (stat.status == 512) { - io.error("Wrong password"); - passWord = null; - return null; - } - try { - ret = JSON.parse(stat.stdout.replace(/\\"/g, '"')); - if (config.keepFormdata) { - formData = ret; - } - return ret; - } - catch(e) { - io.debug({error : e, arguments : arguments}); - io.error("Getting form data failed : " + e.message); - } - } - else { - try { - return JSON.parse(io.read(config.formData)); +function getFormData(callback) //{{{ +{ + var stat, ret; + if (config.useGPG) + { + if (formData !== null) + return formData; + + getPassWord(); + stat = system.spawnSync("gpg2 " + config.GPGOptDecrypt + " --passphrase " + passWord + " --batch --no-tty --yes -d " + config.formData); + if (stat.status == 512) + { + io.error("Wrong password"); + passWord = null; + return null; + } + try + { + ret = JSON.parse(stat.stdout.replace(/\\"/g, '"')); + if (config.keepFormdata) + formData = ret; + + return ret; + } + catch(e) + { + io.debug({error : e, arguments : arguments}); + io.error("Getting form data failed : " + e.message); + } } - catch(e) { - io.debug({error : e, arguments : arguments}); - io.error("Getting form data failed : " + e.message); + else + { + try + { + return JSON.parse(io.read(config.formData)); + } + catch(e) + { + io.debug({error : e, arguments : arguments}); + io.error("Getting form data failed : " + e.message); + } } - } - return null; + return null; }//}}} -function getPassWord() {//{{{ - if (passWord === null) { - passWord = io.prompt("Password :", false); - } +function getPassWord() //{{{ +{ + if (passWord === null) + passWord = io.prompt("Password :", false); }//}}} -function writeFormData(object) {//{{{ - var written = true, ret; - if (config.useGPG) { - getPassWord(); - if (passWord === null) - return false; +function writeFormData(object) //{{{ +{ + var written = true, ret; + if (config.useGPG) + { + getPassWord(); + if (passWord === null) + return false; - ret = system.spawnSync("sh -c \"echo '" + JSON.stringify(object).replace(/"/g, "\\\"") + - "' | gpg2 " + config.GPGOptEncrypt + " --passphrase " + passWord + " --batch --no-tty --yes -c --output " + config.formData + "\""); - if (ret.status == 512) { - io.error("Wrong password"); - password = null; - return false; + ret = system.spawnSync("sh -c \"echo '" + JSON.stringify(object).replace(/"/g, "\\\"") + + "' | gpg2 " + config.GPGOptEncrypt + " --passphrase " + passWord + " --batch --no-tty --yes -c --output " + config.formData + "\""); + if (ret.status == 512) + { + io.error("Wrong password"); + password = null; + return false; + } + written = ret.status === 0; } - written = ret.status === 0; - } - else { - written = io.write(config.formData, "w", JSON.stringify(object, null, 2)); - } - return written; + else + written = io.write(config.formData, "w", JSON.stringify(object, null, 2)); + + return written; }//}}} -function saveForm(form) {//{{{ - var key, object, data, written = false; - var autosubmit = io.prompt("Autosubmit (y/n)?").toLowerCase() == "y" ? true : false; - var saved = false; - if (! system.fileTest(config.formData, FileTest.regular | FileTest.symlink)) { - object = JSON.parse(form); - for (key in object) - break; - object[key].autosubmit = autosubmit; - written = writeFormData(object); - } - else { - object = JSON.parse(form); - data = getFormData(); - if (data) { - for (key in object) - break; - data[key] = object[key]; - data[key].autosubmit = autosubmit; - } - else if (data === null) { - return false; +function saveForm(form) //{{{ +{ + var key, object, data, written = false; + var autosubmit = io.prompt("Autosubmit (y/n)?").toLowerCase() == "y" ? true : false; + var saved = false; + if (! system.fileTest(config.formData, FileTest.regular | FileTest.symlink)) + { + object = JSON.parse(form); + for (key in object) + break; + object[key].autosubmit = autosubmit; + written = writeFormData(object); } - else { - data = object; + else + { + object = JSON.parse(form); + data = getFormData(); + if (data) + { + for (key in object) + break; + data[key] = object[key]; + data[key].autosubmit = autosubmit; + } + else if (data === null) + return false; + else + data = object; + + written = writeFormData(data); } - written = writeFormData(data); - } - return written; + return written; }//}}} -function getForm() {//{{{ - var frames = tabs.current.allFrames; - var form, i, formFound = false; - for (i=0; i<frames.length; i++) { - form = frames[i].inject(util.getBody(injectGetForm)); - if (form != "null") { - if (saveForm(form)) { - io.notify("Form saved"); - } - else { - io.notify("An error occured saving formdata"); - } - formFound = true; - break; +function getForm() //{{{ +{ + var frames = tabs.current.allFrames; + var form, i, formFound = false; + for (i=0; i<frames.length; i++) + { + form = frames[i].inject(util.getBody(injectGetForm)); + if (form != "null") + { + if (saveForm(form)) + io.notify("Form saved"); + else + io.notify("An error occured saving formdata"); + formFound = true; + break; + } } - } - if (!config.keepPassword) - passWord = null; - if (!formFound) - io.error("No storable form found"); + if (!config.keepPassword) + passWord = null; + if (!formFound) + io.error("No storable form found"); }//}}} -function getHasForms(frames) {//{{{ - var i; - for (i=0; i<frames.length; i++) { - if (frames[i].inject("return document.forms.length > 0;") == "true") - return true; - } - return false; +function getHasForms(frames) //{{{ +{ + var i; + for (i=0; i<frames.length; i++) + { + if (frames[i].inject("return document.forms.length > 0;") == "true") + return true; + } + return false; }//}}} -function fillForm() {//{{{ - var data, frames, host, i, ret = false; - if (! system.fileTest(config.formData, FileTest.regular | FileTest.symlink)) { - io.error("No formdata found"); - return; - } - frames = tabs.current.allFrames; - if (!getHasForms(frames)) { - io.error("No form found"); - return; - } - data = getFormData(); - - if (data === null) - return; - for (i=0; i<frames.length; i++) { - host = frames[i].host; - if (data[host]) { - frames[i].inject(util.getBody(injectFillForm), data[host]); +function fillForm() //{{{ +{ + var data, frames, host, i, ret = false; + if (! system.fileTest(config.formData, FileTest.regular | FileTest.symlink)) + { + io.error("No formdata found"); + return; + } + frames = tabs.current.allFrames; + if (!getHasForms(frames)) + { + io.error("No form found"); + return; + } + data = getFormData(); + + if (data === null) + return; + for (i=0; i<frames.length; i++) + { + host = frames[i].host; + if (data[host]) + frames[i].inject(util.getBody(injectFillForm), data[host]); } - } - if (!config.keepPassword) { - passWord = null; - } - io.notify("Executed formfiller"); + if (!config.keepPassword) + passWord = null; + + io.notify("Executed formfiller"); }//}}} // init {{{ return { - init : function (c) { - config = extensions.getConfig(c, defaultConfig); - bind(config.scGetForm, getForm, "formfillerGet"); - bind(config.scFillForm, fillForm, "formfillerFill"); - return true; - }, - end : function () { - unbind("formfillerGet"); - unbind("formfillerFill"); - return true; - } + defaultConfig : defaultConfig, + init : function (c) { + config = c; + bind(config.scGetForm, getForm, "formfillerGet"); + bind(config.scFillForm, fillForm, "formfillerFill"); + return true; + }, + end : function () { + unbind("formfillerGet"); + unbind("formfillerFill"); + return true; + } }//}}} // vim: set ft=javascript: diff --git a/extensions/unique_tabs b/extensions/unique_tabs new file mode 100644 index 00000000..cd52c659 --- /dev/null +++ b/extensions/unique_tabs @@ -0,0 +1,125 @@ +// +// Copyright (c) 2013 Stefan Bolte <portix@gmx.net> +// +// 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, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// + +/*<INFO +Remove duplicate tabs or avoid duplicate tabs by autoswitching to tabs with same url +INFO>*/ + +var defaultConfig = { +//<DEFAULT_CONFIG +// Shortcut that removes duplicate tabs +shortcutRemoveDuplicates : null, + +// Command that removes duplicate tabs +commandRemoveDuplicates : "ut_remove_duplicates", + +// Autofocus a tab if an url is already opened, if the url would be loaded in a +// new tab the new tab is closed. +// Setting this to true makes commandRemoveDuplicates and +// shortcutRemoveDuplicates obsolete because there will be no duplicates. +autoFocus : true, + +// Shortcut for toggling autofocus +shortcutToggleAutoFocus : null, + +// Command for toggling autofocus +commandToggleAutoFocus : "ut_toggle_autofocus", + +//>DEFAULT_CONFIG +}; + +var signalId = -1; + +var removeDuplicates = function() +{ + var uris = []; + for (var i=tabs.length-1; i>=0; --i) + { + var uri = tabs.nth(i).uri; + if (uris.fastIndexOf(uri) == -1) + uris.push(uri); + else + execute((i+1) + "close_tab"); + } +}; +var onNavigation = function(wv, frame, request) +{ + var uri = request.uri; + for (var i=tabs.length-1; i>=0; --i) + { + if (tabs.nth(i).uri == uri && i != wv.number) + { + execute((i+1) + "focus_tab"); + if (/^\s*$/.test(wv.uri)) + execute((wv.number + 1) + "close"); + return true; + } + } + return false; +}; +var connectAutoFocus = function() +{ + signalId = signals.connect("navigation", onNavigation); +}; +var disconnectAutoFocus = function() +{ + if (signalId != -1) + { + signals.disconnect(signalId); + signalId = -1; + return true; + } + return false; +}; +var toggleAutoFocus = function() +{ + if (disconnectAutoFocus()) + io.notify("unique_tabs: autofocus disabled"); + else + { + io.notify("unique_tabs: autofocus enabled"); + connectAutoFocus(); + } +}; + +return { + defaultConfig : defaultConfig, + init : function(c) { + if (c.shortcutRemoveDuplicates || c.commandRemoveDuplicates) + { + bind(c.shortcutRemoveDuplicates, removeDuplicates, c.commandRemoveDuplicates); + } + if (c.shortcutToggleAutoFocus || c.commandToggleAutoFocus) + { + bind(c.shortcutToggleAutoFocus, toggleAutoFocus, c.commandToggleAutoFocus); + } + + if (c.autoFocus) + connectAutoFocus(); + return true; + }, + end : function() { + unbind(removeDuplicates); + unbind(toggleAutoFocus); + disconnectAutoFocus(); + return true; + } +} + +// vim:set ft=javascript: diff --git a/extensions/userscripts b/extensions/userscripts index e256bbe6..14b98a59 100644 --- a/extensions/userscripts +++ b/extensions/userscripts @@ -70,7 +70,7 @@ var sigCommitted = -1; /* //<DEFAULT_CONFIG // paths to userscripts, this extension will also load all scripts in from - // $XDG_CONFIG_HOME/.config/dwb/scripts + // $XDG_CONFIG_HOME/scripts scripts : [] //>DEFAULT_CONFIG */ @@ -85,449 +85,471 @@ UserScript.prototype = new Function(); UserScript.prototype.constructor = UserScript; function UserScript() { - this.description = null; - this.downloadURL = null; - this.exclude = []; - this.icon = null; - this.include = []; - this.match = []; - this.name = null; - this.namespace = null; - this.require = []; - this.resource = []; - this.runAt = "document-end"; - this.unwrap = false; - this.updateURL = null; - this.version = null; - this.script = null; - this.scriptId = null; - // scriptish - this.delay = 0; - this.noframes = false; - this.priority = 0; + this.description = null; + this.downloadURL = null; + this.exclude = []; + this.icon = null; + this.include = []; + this.match = []; + this.name = null; + this.namespace = null; + this.require = []; + this.resource = []; + this.runAt = "document-end"; + this.unwrap = false; + this.updateURL = null; + this.version = null; + this.script = null; + this.scriptId = null; + // scriptish + this.delay = 0; + this.noframes = false; + this.priority = 0; } // Reused regular expressions var regexes = { - isTld : /\.tld(?:\/|:|$)/, - isRegExp : /^\/.*\/$/ + isTld : /\.tld(?:\/|:|$)/, + isRegExp : /^\/.*\/$/ }; -var GM_compatability = function () { - var DWB_scriptPrefix = "dwb_userscript_"; - if (DWB_scriptId !== undefined) { - DWB_scriptPrefix = DWB_scriptPrefix + DWB_scriptId + "_"; - } - var GM_addStyle = function (styles) - { - var style = document.createElement("style"); - style.setAttribute("type", "text/css"); - style.appendChild(document.createTextNode(styles)); - document.getElementsByTagName("head")[0].appendChild(style); - }; - var GM_log = function (text) { - console.log(text); - }; - var GM_setValue = function (key, value) { - if (localStorage !== null && (typeof value === "string" || typeof value === "number" || typeof value == "boolean") ) - localStorage.setItem(DWB_scriptPrefix + key, value); - else - GM_log("GM_setValue only works with enabled localStorage and only for strings, numbers and booleans"); - }; - var GM_getValue = function (key, def) { - if (localStorage !== null) - return localStorage.getItem(DWB_scriptPrefix + key) || def; - else - GM_log("GM_getValue only works with enabled localStorage"); - return undefined; - }; - var GM_deleteValue = function (key) { - if (localStorage !== null) - localStorage.removeItem(DWB_scriptPrefix + key); - else - GM_log("GM_deleteValue only works with enabled localStorage"); - }; - var GM_listValues = function () { - var i; - var a = []; - for (i=0; i<localStorage.length; i++) { - a.push(localStorage.key(i).replace(DWB_scriptPrefix, "")); +var GM_compatability = function () +{ + var DWB_scriptPrefix = "dwb_userscript_"; + if (DWB_scriptId !== undefined) + { + DWB_scriptPrefix = DWB_scriptPrefix + DWB_scriptId + "_"; } - return a; - }; - var GM_info = function () { return undefined; }; - var GM_registerMenuCommand = function () { return undefined; }; - var GM_openInTab = function (url) { return null; }; - var GM_getResourceText = function (name) { - // TODO implemnt - return ""; - }; - var GM_getResourceURL = function (name) { - // TODO implemnt - return ""; - }; - var GM_xmlHttpRequest = function (details) { - // TODO implemnt - return null; - }; + var GM_addStyle = function (styles) + { + var style = document.createElement("style"); + style.setAttribute("type", "text/css"); + style.appendChild(document.createTextNode(styles)); + document.getElementsByTagName("head")[0].appendChild(style); + }; + var GM_log = function (text) + { + console.log(text); + }; + var GM_setValue = function (key, value) + { + if (localStorage !== null && (typeof value === "string" || typeof value === "number" || typeof value == "boolean") ) + localStorage.setItem(DWB_scriptPrefix + key, value); + else + GM_log("GM_setValue only works with enabled localStorage and only for strings, numbers and booleans"); + }; + var GM_getValue = function (key, def) + { + if (localStorage !== null) + return localStorage.getItem(DWB_scriptPrefix + key) || def; + else + GM_log("GM_getValue only works with enabled localStorage"); + return undefined; + }; + var GM_deleteValue = function (key) + { + if (localStorage !== null) + localStorage.removeItem(DWB_scriptPrefix + key); + else + GM_log("GM_deleteValue only works with enabled localStorage"); + }; + var GM_listValues = function () + { + var i; + var a = []; + for (i=0; i<localStorage.length; i++) { + a.push(localStorage.key(i).replace(DWB_scriptPrefix, "")); + } + return a; + }; + var GM_info = function () { return undefined; }; + var GM_registerMenuCommand = function () { return undefined; }; + var GM_openInTab = function (url) { return null; }; + var GM_getResourceText = function (name) + { + // TODO implemnt + return ""; + }; + var GM_getResourceURL = function (name) + { + // TODO implemnt + return ""; + }; + var GM_xmlHttpRequest = function (details) + { + // TODO implemnt + return null; + }; }; function matchIncludeExclude(frame, items) //{{{ { - var uri = frame.uri; - var domain = frame.domain; - var i; - for (i=0; i<items.length; i++) { - try { - if (items[i].isTld && domain !== null) { - var reg = new RegExp("(?=.)" + domain + "($|/|:)"); - var newDomain = domain.substring(0, domain.indexOf(".")) + ".tld$1"; - uri = uri.replace(reg, newDomain); - } - if (items[i].regExp.test(uri)) { - return true; - } - } - catch(e) { - extensions.error(me, e); + var uri = frame.uri; + var domain = frame.domain; + var i; + for (i=0; i<items.length; i++) + { + try + { + if (items[i].isTld && domain !== null) + { + var reg = new RegExp("(?=.)" + domain + "($|/|:)"); + var newDomain = domain.substring(0, domain.indexOf(".")) + ".tld$1"; + uri = uri.replace(reg, newDomain); + } + if (items[i].regExp.test(uri)) + return true; + } + catch(e) + { + extensions.error(me, e); + } } - } - return false; + return false; }//}}} function matchMatches(frame, items) //{{{ { - var i, item; - var o = uriSplit(frame.uri); - if (o === null) - return false; - for (i=0; i<items.length; i++) { - item = items[i]; - if (item.allUrls || - (item.scheme.test(o.scheme) && - item.host.test(o.host) && - (o.path === null || item.path.test(o.path)))) { - return true; - } + var i, item; + var o = uriSplit(frame.uri); + if (o === null) + return false; + for (i=0; i<items.length; i++) + { + item = items[i]; + if (item.allUrls || + (item.scheme.test(o.scheme) && + item.host.test(o.host) && + (o.path === null || item.path.test(o.path)))) + { + return true; + } - } + } return false; }//}}} -function doInject(frame, item) { - if (item.delay > 0) { - timerStart(item.delay, function() { - frame.inject(item.script, null, item.unwrap); - return false; - }); +function doInject(frame, item) +{ + if (item.delay > 0) + { + timerStart(item.delay, function() { + frame.inject(item.script, null, item.unwrap); + return false; + }); } - else { + else frame.inject(item.script, null, item.unwrap); - } } function handle(frame, array, isMainFrame) //{{{ { - var i, item; - for (i=0; i<array.length; i++) { - item = array[i]; - if (item.noframes && !isMainFrame) - continue; - try { - if (matchIncludeExclude(frame, item.exclude)) - continue; - - if (matchIncludeExclude(frame, item.include)) { - doInject(frame, item); - } - else if (matchMatches(frame, item.match)) { - doInject(frame, item); - } - } - catch (e) { - extensions.debug(me); + var i, item; + for (i=0; i<array.length; i++) + { + item = array[i]; + if (item.noframes && !isMainFrame) + continue; + try + { + if (matchIncludeExclude(frame, item.exclude)) + continue; + + if (matchIncludeExclude(frame, item.include)) + doInject(frame, item); + else if (matchMatches(frame, item.match)) + doInject(frame, item); + } + catch (e) + { + extensions.debug(me); + } } - } }//}}} function loadFinishedCallback(wv, frame) //{{{ { - handle(frame, onEnd, wv.mainFrame === frame); + handle(frame, onEnd, wv.mainFrame === frame); }//}}} function loadCommittedCallback(wv) //{{{ { - handle(wv.mainFrame, onStart, true); + handle(wv.mainFrame, onStart, true); }//}}} function parseIncludeExclude(array) //{{{ { - var i, rule; - for (i=0; i<array.length; i++) { - rule = array[i]; - if (regexes.isRegExp.test(rule)) { - array[i] = { - regExp : new RegExp(rule.substring(1, rule.length-1)), - isTld : regexes.isTld.test(rule) - }; - } - else { - array[i] = { - regExp : new RegExp(rule.replace(/\*/g, ".*")), - isTld : regexes.isTld.test(rule) - }; + var i, rule; + for (i=0; i<array.length; i++) + { + rule = array[i]; + if (regexes.isRegExp.test(rule)) + { + array[i] = { + regExp : new RegExp(rule.substring(1, rule.length-1)), + isTld : regexes.isTld.test(rule) + }; + } + else + { + array[i] = { + regExp : new RegExp(rule.replace(/\*/g, ".*")), + isTld : regexes.isTld.test(rule) + }; + } } - } }//}}} function uriSplit(uri) { - var parts, scheme, host, path, idx; - parts = uri.split("://"); - if (parts[0] === uri) - return null; - scheme = parts[0]; - idx = parts[1].indexOf("/"); - if (idx == -1) { - host = parts[1]; - path = null; - } - else { - host = parts[1].substring(0, idx); - path = parts[1].substring(idx); - } - return { path : path, host: host, scheme : scheme }; + var parts, scheme, host, path, idx; + parts = uri.split("://"); + if (parts[0] === uri) + return null; + scheme = parts[0]; + idx = parts[1].indexOf("/"); + if (idx == -1) + { + host = parts[1]; + path = null; + } + else + { + host = parts[1].substring(0, idx); + path = parts[1].substring(idx); + } + return { path : path, host: host, scheme : scheme }; } function parseMatch(m) { - var i, scheme, host, path, parts, j; - if (m === "<all_urls>") { - return { allUrls : true }; - } + var i, scheme, host, path, parts, j; + if (m === "<all_urls>") { + return { allUrls : true }; + } - var o = uriSplit(m); - if (o === null) { - extensions.warning(me, "Invalid or unsupported match rule: " + m); - return null; - } - if (!(/\*|http|https|file/.test(o.scheme))) { - extensions.warning(me, "Invalid scheme pattern: " + m); - return null; - } - else { - o.scheme = new RegExp(o.scheme.replace("*", ".*")); - } - if (! (/^(?:\*\.[^*\/]*|[^*]*|\*)$/.test(o.host))) { - extensions.warning(me, "Invalid host pattern: " + m); - return null; - } - else { - o.host = new RegExp(o.host.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace("*", ".*")); - } - if (! (/^\/.*/.test(o.path))) { - extensions.warning(me, "Invalid path pattern: " + m); - return null; - } - else if (o.path !== null) { - o.path = new RegExp(o.path.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace("*", ".*")); - } - return { host : o.host, scheme : o.scheme, path : o.path, allUrls : false }; + var o = uriSplit(m); + if (o === null) + { + extensions.warning(me, "Invalid or unsupported match rule: " + m); + return null; + } + if (!(/\*|http|https|file/.test(o.scheme))) + { + extensions.warning(me, "Invalid scheme pattern: " + m); + return null; + } + else + o.scheme = new RegExp(o.scheme.replace("*", ".*")); + + if (! (/^(?:\*\.[^*\/]*|[^*]*|\*)$/.test(o.host))) + { + extensions.warning(me, "Invalid host pattern: " + m); + return null; + } + else + o.host = new RegExp(o.host.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace("*", ".*")); + + if (! (/^\/.*/.test(o.path))) + { + extensions.warning(me, "Invalid path pattern: " + m); + return null; + } + else if (o.path !== null) + o.path = new RegExp(o.path.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1").replace("*", ".*")); + + return { host : o.host, scheme : o.scheme, path : o.path, allUrls : false }; } -function getRequirements(userscript) {//{{{ - var i; - for (i=0; i<userscript.require.length; i++) { - //io.print(userscript.require[i]); - //sendRequest(userscript.require[0], function (response, message) { - // ; - // //io.print(response); - //}); - //sendRequest(userscripts.require[i], function (response) { - // io.print(response); - //}); - } +function getRequirements(userscript) //{{{ +{ + var i; + for (i=0; i<userscript.require.length; i++) { + //io.print(userscript.require[i]); + //sendRequest(userscript.require[0], function (response, message) { + // ; + // //io.print(response); + //}); + //sendRequest(userscripts.require[i], function (response) { + // io.print(response); + //}); + } }//}}} function parseScript(path) //{{{ { - var userscript; - var i, items, key, value, idx, m, matches = [], numVal; + var userscript; + var i, items, key, value, idx, m, matches = [], numVal; - var script = io.read(path); - if (!script || (/^\s*$/).test(script)) - return; + var script = io.read(path); + if (!script || (/^\s*$/).test(script)) + return; - var metaStart = script.search(/(^|\n)\/\/\s*==UserScript==/); - var metaEnd = script.search(/\n\/\/\s*==\/UserScript==/); + var metaStart = script.search(/(^|\n)\/\/\s*==UserScript==/); + var metaEnd = script.search(/\n\/\/\s*==\/UserScript==/); - userscript = new UserScript(); + userscript = new UserScript(); - if (metaStart == -1 || metaEnd == -1) { - userscript.script = script; - userscript.include = [ { regExp : /.*/, isTld : false} ]; - onEnd.push(userscript); - return; - } + if (metaStart == -1 || metaEnd == -1) + { + userscript.script = script; + userscript.include = [ { regExp : /.*/, isTld : false} ]; + onEnd.push(userscript); + return; + } - var meta = script.substring(metaStart, metaEnd).split("\n"); - var scriptStart = script.substring(metaEnd+1).indexOf("\n") + metaEnd + 1; - - var regValue = /\s[^\/@]\S?/; - var regIsRule = /^\s*\/\/\s*@/; - for (i=1; i<meta.length; i++) { - if (! (regIsRule.test(meta[i])) ) - continue; - try { - items = meta[i].split(/\s+/, 2); - key = items[1].substring(1).trim(); - idx = meta[i].search(regValue); - value = idx >= 0 ? meta[i].substring(idx+1).trim() : null; - if (key == "description" || - key == "downloadURL" || - key == "icon" || - key == "name" || - key == "namespace" || - key == "updateURL" || - key == "version") - { - userscript[key] = value; - } - else if (typeof userscript[key] == "number") { - try { - numVal = parseInt(value, 10); - if (!isNaN(numVal)) - userscript[key] = numVal; + var meta = script.substring(metaStart, metaEnd).split("\n"); + var scriptStart = script.substring(metaEnd+1).indexOf("\n") + metaEnd + 1; + + var regValue = /\s[^\/@]\S?/; + var regIsRule = /^\s*\/\/\s*@/; + for (i=1; i<meta.length; i++) + { + if (! (regIsRule.test(meta[i])) ) + continue; + try + { + items = meta[i].split(/\s+/, 2); + key = items[1].substring(1).trim(); + idx = meta[i].search(regValue); + value = idx >= 0 ? meta[i].substring(idx+1).trim() : null; + if (key == "description" || + key == "downloadURL" || + key == "icon" || + key == "name" || + key == "namespace" || + key == "updateURL" || + key == "version") + { + userscript[key] = value; + } + else if (typeof userscript[key] == "number") + { + try + { + numVal = parseInt(value, 10); + if (!isNaN(numVal)) + userscript[key] = numVal; + } + catch (e) { + extensions.debug(me, e); + } + } + else if (key == "unwrap") + userscript.unwrap = true; + else if (key == "noframes") + userscript.noframes = true; + else if (key == "run-at") + userscript.runAt = value; + else if (userscript[key] instanceof Array) + userscript[key] = userscript[key].concat(value.match(/\S+/g)); } - catch (e) { - extensions.debug(me, e); + catch(e) + { + extensions.debug(me, e); } - } - else if (key == "unwrap") - userscript.unwrap = true; - else if (key == "noframes") - userscript.noframes = true; - else if (key == "run-at") - userscript.runAt = value; - else if (userscript[key] instanceof Array) { - userscript[key] = userscript[key].concat(value.match(/\S+/g)); - } - } - catch(e) { - extensions.debug(me, e); - } - } - if (userscript.include.length === 0) - userscript.include.push({regExp : /.*/, isTld : false}); - else - parseIncludeExclude(userscript.include); - parseIncludeExclude(userscript.exclude); - // TODO resources - - var scriptId = new String(); - if (userscript.namespace === null || userscript.name === null) - userscript.scriptId = path; - else - userscript.scriptId = userscript.namespace + "::" + userscript.name; - userscript.scriptId = userscript.scriptId.replace(/\s+/g, "_"); - userscript.script = "var DWB_scriptId = '" + userscript.scriptId + "';" + - util.getBody(GM_compatability) + script.substring(0, metaStart) + - script.substring(scriptStart); - - getRequirements(userscript); - - for (i=0; i<userscript.match.length; i++) { - m = parseMatch(userscript.match[i]); - if (m !== null) { - matches.push(m); } - } - userscript.match = matches; - if (userscript.runAt == "document-start") - onStart.push(userscript); - else - onEnd.push(userscript); + if (userscript.include.length === 0) + userscript.include.push({regExp : /.*/, isTld : false}); + else + parseIncludeExclude(userscript.include); + parseIncludeExclude(userscript.exclude); + // TODO resources + + var scriptId = new String(); + if (userscript.namespace === null || userscript.name === null) + userscript.scriptId = path; + else + userscript.scriptId = userscript.namespace + "::" + userscript.name; + userscript.scriptId = userscript.scriptId.replace(/\s+/g, "_"); + userscript.script = "var DWB_scriptId = '" + userscript.scriptId + "';" + + util.getBody(GM_compatability) + script.substring(0, metaStart) + + script.substring(scriptStart); + + getRequirements(userscript); + + for (i=0; i<userscript.match.length; i++) + { + m = parseMatch(userscript.match[i]); + if (m !== null) + matches.push(m); + } + userscript.match = matches; + if (userscript.runAt == "document-start") + onStart.push(userscript); + else + onEnd.push(userscript); }//}}} -function userscriptsStart() { - var ret = false; - if (onStart.length > 0) { - onStart.sort(function(a, b) { return b.priority - a.priority; }); - sigCommitted = signals.connect("loadCommitted", loadCommittedCallback); - ret = true; - } - if (onEnd.length > 0) { - onEnd.sort(function(a, b) { return b.priority - a.priority; }); - sigDocument = signals.connect("documentLoaded", loadFinishedCallback); - ret = true; - } - //metaData = {}; - //onStart.concat(onEnd).forEach(function (v, k, obj) { - // var o = { - // require : obj[k].require, - // downloadURL : obj[k].downloadURL, - // updateURL : obj[k].updateURL, - // version : obj[k].version, - // resource : obj[k].resource - // }; - // metaData[obj[k].scriptId] = o; - //}); - //io.write(META_DATA, "w", JSON.stringify(metaData)); - return ret; +function userscriptsStart() +{ + var ret = false; + if (onStart.length > 0) + { + onStart.sort(function(a, b) { return b.priority - a.priority; }); + sigCommitted = signals.connect("loadCommitted", loadCommittedCallback); + ret = true; + } + if (onEnd.length > 0) + { + onEnd.sort(function(a, b) { return b.priority - a.priority; }); + sigDocument = signals.connect("documentLoaded", loadFinishedCallback); + ret = true; + } + //metaData = {}; + //onStart.concat(onEnd).forEach(function (v, k, obj) { + // var o = { + // require : obj[k].require, + // downloadURL : obj[k].downloadURL, + // updateURL : obj[k].updateURL, + // version : obj[k].version, + // resource : obj[k].resource + // }; + // metaData[obj[k].scriptId] = o; + //}); + //io.write(META_DATA, "w", JSON.stringify(metaData)); + return ret; } function parseScripts(scripts) //{{{ { - var i, path; - var scriptDir = data.configDir + "/scripts"; - for (i=0; i<scripts.length; i++) { - if (system.fileTest(scripts[i], FileTest.regular | FileTest.symlink)) { - parseScript(scripts[i]); + var i, path; + var scriptDir = data.configDir + "/scripts"; + for (i=0; i<scripts.length; i++) + { + if (system.fileTest(scripts[i], FileTest.regular | FileTest.symlink)) + parseScript(scripts[i]); } - } - if (system.fileTest(scriptDir, FileTest.dir)) { - var lines = io.dirNames(scriptDir); - for (i=0; i<lines.length; i++) { - if (lines[i].charAt(0) == ".") - continue; - path = scriptDir + "/" + lines[i]; - if (!(/^\s*$/.test(lines[i])) && system.fileTest(path, FileTest.regular | FileTest.symlink)) { - parseScript(path); - } + if (system.fileTest(scriptDir, FileTest.dir)) + { + var lines = io.dirNames(scriptDir); + for (i=0; i<lines.length; i++) + { + if (lines[i].charAt(0) == ".") + continue; + path = scriptDir + "/" + lines[i]; + if (!(/^\s*$/.test(lines[i])) && system.fileTest(path, FileTest.regular | FileTest.symlink)) + parseScript(path); + } } - } - return userscriptsStart(); + return userscriptsStart(); }//}}} return { - init : function (c) { - //var meta; - //if (! system.fileTest(DATA_DIR, FileTest.dir)) { - // system.mkdir(DATA_DIR, 0700); - //} - //if (system.fileTest(META_DATA, FileTest.exists)) { - // meta = io.read(META_DATA); - // try { - // metaData = JSON.parse(meta); - // } - // catch (e) { - // io.debug(e); - // metaData = {}; - // } - //} - return parseScripts(c ? c.scripts || [] : []); - }, - end : function () { - if (sigDocument >= 0) { - signals.disconnect(sigDocument); - sigDocument = -1; - } - if (sigCommitted >= 0) { - signals.disconnect(sigCommitted); - sigCommitted = -1; + init : function (c) { + return parseScripts(c ? c.scripts || [] : []); + }, + end : function () { + if (sigDocument >= 0) { + signals.disconnect(sigDocument); + sigDocument = -1; + } + if (sigCommitted >= 0) { + signals.disconnect(sigCommitted); + sigCommitted = -1; + } + } - - } }; // vim: set ft=javascript: diff --git a/scripts/lib/dwb.js b/scripts/lib/dwb.js index 5111c9a4..a734c7e5 100644 --- a/scripts/lib/dwb.js +++ b/scripts/lib/dwb.js @@ -171,7 +171,7 @@ { var sig = this.connect(name, (function() { this.blockSignal(sig); - var result = callback.apply(null, arguments); + var result = callback.apply(callback, arguments); this.unblockSignal(sig); return result; }).bind(this)); diff --git a/scripts/lib/signals.js b/scripts/lib/signals.js index dffb429f..a89b1f14 100644 --- a/scripts/lib/signals.js +++ b/scripts/lib/signals.js @@ -86,17 +86,17 @@ value : function(name, callback) { this.connect("createTab", function(wv) { - wv.connect(name, function() { callback.apply(null, arguments);}); + wv.connect(name, function() { callback.apply(callback, arguments);}); }); } }, "disconnect" : { - value : _disconnectByProp.bind(this, "id") + value : _disconnectByProp.bind(null, "id") }, "disconnectByFunction" : { - value : _disconnectByProp.bind(this, "callback") + value : _disconnectByProp.bind(null, "callback") }, "disconnectByName" : { diff --git a/scripts/lib/util.js b/scripts/lib/util.js index 0d9986d1..f104628e 100644 --- a/scripts/lib/util.js +++ b/scripts/lib/util.js @@ -73,7 +73,6 @@ }); Object.freeze(util); - if (Object.prototype.forEach === undefined) { Object.defineProperty(Object.prototype, "forEach", diff --git a/src/adblock.c b/src/adblock.c index e6b65f4b..328403f4 100644 --- a/src/adblock.c +++ b/src/adblock.c @@ -23,6 +23,7 @@ #include "domain.h" #include "adblock.h" #include "js.h" +#include "dom.h" @@ -435,7 +436,7 @@ adblock_frame_load_status_cb(WebKitWebFrame *frame, GParamSpec *p, GList *gl) if (status == WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT) adblock_apply_element_hider(frame, gl); else if (status == WEBKIT_LOAD_COMMITTED) - dwb_dom_add_frame_listener(frame, "beforeload", G_CALLBACK(adblock_before_load_cb), true, gl); + dom_add_frame_listener(frame, "beforeload", G_CALLBACK(adblock_before_load_cb), true, gl); } /* adblock_frame_created_cb {{{*/ diff --git a/src/commands.c b/src/commands.c index b8de8ffb..4b885fd3 100644 --- a/src/commands.c +++ b/src/commands.c @@ -30,6 +30,8 @@ #include "download.h" #include "js.h" #include "scripts.h" +#include "editor.h" +#include "dom.h" inline static int dwb_floor(double x) { @@ -54,6 +56,21 @@ commands_simple_command(KeyMap *km) completion_clean_autocompletion(); } + if (EMIT_SCRIPT(EXECUTE_COMMAND)) + { + char *json = util_create_json(3, + CHAR, "command", km->map->n.first, + CHAR, "argument", arg->p, + INTEGER, "nummod", dwb.state.nummod); + ScriptSignal sig = { NULL, SCRIPTS_SIG_META(json, EXECUTE_COMMAND, 0) } ; + + gboolean prevent = scripts_emit(&sig); + g_free(json); + + if (prevent) + return STATUS_OK; + } + ret = func(km, arg); switch (ret) { @@ -770,7 +787,7 @@ commands_fullscreen(KeyMap *km, Arg *arg) DwbStatus commands_open_editor(KeyMap *km, Arg *arg) { - return dwb_open_in_editor(); + return editor_open(); }/*}}}*/ /* dwb_command_mode {{{*/ @@ -803,7 +820,7 @@ commands_set_bars(int status) gtk_widget_set_visible(dwb.gui.topbox, (status & BAR_VIS_TOP) && (GET_BOOL("show-single-tab") || dwb.state.views->next)); gtk_widget_set_visible(dwb.gui.bottombox, status & BAR_VIS_STATUS); if ((status & BAR_VIS_STATUS) ) - dwb_dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->hover.element), NULL); + dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->hover.element), NULL); } /* commands_toggle_bars {{{*/ DwbStatus diff --git a/src/config.h b/src/config.h index 81c6cb09..9f126c95 100644 --- a/src/config.h +++ b/src/config.h @@ -1170,4 +1170,6 @@ static WebSettings DWB_SETTINGS[] = { SETTING_GLOBAL, BOOLEAN, { .b = false }, NULL, { 0 }, }, { { "print-previewer", "Command used for the printing preview", }, SETTING_GLOBAL, CHAR, { .p = NULL }, NULL, { 0 }, }, + { { "accept-language", "If set will be used for 'Accept-Language' header in all requests", }, + SETTING_GLOBAL | SETTING_ONINIT, CHAR, { .p = NULL }, (S_Func)dwb_set_accept_language, { 0 }, }, };/*}}}*/ diff --git a/src/dom.c b/src/dom.c new file mode 100644 index 00000000..60885d51 --- /dev/null +++ b/src/dom.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <string.h> +#include "dwb.h" + +gboolean +dom_add_frame_listener(WebKitWebFrame *frame, const char *signal, GCallback callback, gboolean bubble, GList *gl) +{ + char *framesrc, *tagname; + gboolean ret = false; + WebKitDOMDOMWindow *win; + WebKitDOMNode *node; + WebKitWebView *wv = webkit_web_frame_get_web_view(frame); + WebKitDOMDocument *doc = webkit_web_view_get_dom_document(wv); + const char *src = webkit_web_frame_get_uri(frame); + if (g_strcmp0(src, "about:blank")) + { + /* We have to find the correct frame, but there is no access from the web_frame + * to the Htmlelement */ + WebKitDOMNodeList *frames = webkit_dom_document_query_selector_all(doc, "iframe, frame", NULL); + for (guint i=0; i<webkit_dom_node_list_get_length(frames) && ret == false; i++) + { + node = webkit_dom_node_list_item(frames, i); + tagname = webkit_dom_node_get_node_name(node); + if (!g_ascii_strcasecmp(tagname, "iframe")) + { + framesrc = webkit_dom_html_iframe_element_get_src(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node)); + win = webkit_dom_html_iframe_element_get_content_window(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node)); + } + else + { + framesrc = webkit_dom_html_frame_element_get_src(WEBKIT_DOM_HTML_FRAME_ELEMENT(node)); + win = webkit_dom_html_frame_element_get_content_window(WEBKIT_DOM_HTML_FRAME_ELEMENT(node)); + } + if (!g_strcmp0(src, framesrc)) + ret = webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(win), signal, callback, true, gl); + + g_free(framesrc); + } + g_object_unref(frames); + } + return ret; +} + +/* dwb_get_editable(WebKitDOMElement *) {{{*/ +gboolean +dom_get_editable(WebKitDOMElement *element) +{ + if (element == NULL) + return false; + + char *tagname = webkit_dom_node_get_node_name(WEBKIT_DOM_NODE(element)); + if (tagname == NULL) + return false; + if (!strcasecmp(tagname, "INPUT")) + { + char *type = webkit_dom_element_get_attribute((void*)element, "type"); + if (!g_strcmp0(type, "text") || !g_strcmp0(type, "search")|| !g_strcmp0(type, "password")) + return true; + } + else if (!strcasecmp(tagname, "TEXTAREA")) + return true; + + return false; +}/*}}}*/ + +/* dom_get_active_element(WebKitDOMDocument ) {{{*/ +WebKitDOMElement * +dom_get_active_element(WebKitDOMDocument *doc) +{ + WebKitDOMElement *ret = NULL; + WebKitDOMDocument *d = NULL; + + WebKitDOMElement *active = webkit_dom_html_document_get_active_element((void*)doc); + char *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)); + ret = dom_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)); + ret = dom_get_active_element(d); + } + else + ret = active; + + return ret; +}/*}}}*/ + + +/* dwb_dom_remove_from_parent(WebKitDOMNode *node, GError **error) {{{*/ +gboolean +dom_remove_from_parent(WebKitDOMNode *node, GError **error) +{ + WebKitDOMNode *parent = webkit_dom_node_get_parent_node(node); + if (parent != NULL) + { + webkit_dom_node_remove_child(parent, node, error); + return true; + } + return false; +}/*}}}*/ diff --git a/src/dom.h b/src/dom.h new file mode 100644 index 00000000..e1b4ed4a --- /dev/null +++ b/src/dom.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DOM_H +#define DOM_H + +gboolean dom_add_frame_listener(WebKitWebFrame *frame, const char *signal, GCallback callback, gboolean bubble, GList *gl); +gboolean dom_get_editable(WebKitDOMElement *element); +WebKitDOMElement * dom_get_active_element(WebKitDOMDocument *doc); +gboolean dom_remove_from_parent(WebKitDOMNode *node, GError **error); +#endif @@ -47,6 +47,7 @@ #include "domain.h" #include "application.h" #include "scripts.h" +#include "dom.h" /* DECLARATIONS {{{*/ static DwbStatus dwb_webkit_setting(GList *, WebSettings *); @@ -77,6 +78,7 @@ static DwbStatus dwb_set_ntlm(GList *gl, WebSettings *s); static DwbStatus dwb_set_find_delay(GList *gl, WebSettings *s); static DwbStatus dwb_set_do_not_track(GList *gl, WebSettings *s); static DwbStatus dwb_set_show_single_tab(GList *gl, WebSettings *s); +static DwbStatus dwb_set_accept_language(GList *gl, WebSettings *s); #ifdef WITH_LIBSOUP_2_38 static DwbStatus dwb_set_dns_lookup(GList *gl, WebSettings *s); #endif @@ -98,13 +100,6 @@ static void dwb_init_gui(void); static Navigation * dwb_get_search_completion(const char *text); static void dwb_clean_vars(void); -typedef struct _EditorInfo { - char *filename; - char *id; - GList *gl; - WebKitDOMElement *element; - char *tagname; -} EditorInfo; typedef struct _UserScriptEnv { GIOChannel *channel; @@ -175,6 +170,14 @@ dwb_set_adblock(GList *gl, WebSettings *s) } }/*}}}*/ +/*{{{*/ +static DwbStatus +dwb_set_accept_language(GList *gl, WebSettings *s) +{ + g_object_set(webkit_get_default_session(), "accept-language", s->arg_local.p, NULL); + return STATUS_OK; +}/*}}}*/ + /* dwb_set_cookies {{{ */ static DwbStatus dwb_set_cookies(GList *gl, WebSettings *s) { @@ -584,7 +587,7 @@ dwb_hide_message() if (gtk_widget_get_visible(dwb.gui.bottombox)) CLEAR_COMMAND_TEXT(); else - dwb_dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); + dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); } return NULL; }/*}}}*/ @@ -1049,241 +1052,12 @@ dwb_scroll(GList *gl, double step, ScrollDirection dir) gtk_adjustment_set_value(a, scroll); }/*}}}*/ -/* dwb_editor_watch (GChildWatchFunc) {{{*/ -static void -dwb_editor_watch(GPid pid, int status, EditorInfo *info) -{ - gsize length; - WebKitDOMElement *e = NULL; - WebKitWebView *wv; - - char *content = util_get_file_content(info->filename, &length); - - if (content == NULL) - goto clean; - - if (!info->gl || !g_list_find(dwb.state.views, info->gl->data)) - { - if (info->id == NULL) - goto clean; - else - wv = CURRENT_WEBVIEW(); - } - else - wv = WEBVIEW(info->gl); - if (info->id != NULL) - { - WebKitDOMDocument *doc = webkit_web_view_get_dom_document(wv); - e = webkit_dom_document_get_element_by_id(doc, info->id); - - if (e == NULL && (e = info->element) == NULL ) - goto clean; - } - else - e = info->element; - - /* g_file_get_contents adds an additional newline */ - if (length > 0 && content[length-1] == '\n') - content[length-1] = 0; - - if (!strcasecmp(info->tagname, "INPUT")) - webkit_dom_html_input_element_set_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(e), content); - else if (!strcasecmp(info->tagname, "TEXTAREA")) - webkit_dom_html_text_area_element_set_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(e), content); - -clean: - unlink(info->filename); - g_free(info->filename); - g_free(info->id); - g_free(info); -}/*}}}*/ - -/* dwb_dom_remove_from_parent(WebKitDOMNode *node, GError **error) {{{*/ -gboolean -dwb_dom_remove_from_parent(WebKitDOMNode *node, GError **error) -{ - WebKitDOMNode *parent = webkit_dom_node_get_parent_node(node); - if (parent != NULL) - { - webkit_dom_node_remove_child(parent, node, error); - return true; - } - return false; -}/*}}}*/ - - -gboolean -dwb_dom_add_frame_listener(WebKitWebFrame *frame, const char *signal, GCallback callback, gboolean bubble, GList *gl) -{ - char *framesrc, *tagname; - gboolean ret = false; - WebKitDOMDOMWindow *win; - WebKitDOMNode *node; - WebKitWebView *wv = webkit_web_frame_get_web_view(frame); - WebKitDOMDocument *doc = webkit_web_view_get_dom_document(wv); - const char *src = webkit_web_frame_get_uri(frame); - if (g_strcmp0(src, "about:blank")) - { - /* We have to find the correct frame, but there is no access from the web_frame - * to the Htmlelement */ - WebKitDOMNodeList *frames = webkit_dom_document_query_selector_all(doc, "iframe, frame", NULL); - for (guint i=0; i<webkit_dom_node_list_get_length(frames) && ret == false; i++) - { - node = webkit_dom_node_list_item(frames, i); - tagname = webkit_dom_node_get_node_name(node); - if (!g_ascii_strcasecmp(tagname, "iframe")) - { - framesrc = webkit_dom_html_iframe_element_get_src(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node)); - win = webkit_dom_html_iframe_element_get_content_window(WEBKIT_DOM_HTML_IFRAME_ELEMENT(node)); - } - else - { - framesrc = webkit_dom_html_frame_element_get_src(WEBKIT_DOM_HTML_FRAME_ELEMENT(node)); - win = webkit_dom_html_frame_element_get_content_window(WEBKIT_DOM_HTML_FRAME_ELEMENT(node)); - } - if (!g_strcmp0(src, framesrc)) - ret = webkit_dom_event_target_add_event_listener(WEBKIT_DOM_EVENT_TARGET(win), signal, callback, true, gl); - - g_free(framesrc); - } - g_object_unref(frames); - } - return ret; -} - -/* dwb_get_editable(WebKitDOMElement *) {{{*/ -static gboolean -dwb_get_editable(WebKitDOMElement *element) -{ - if (element == NULL) - return false; - - char *tagname = webkit_dom_node_get_node_name(WEBKIT_DOM_NODE(element)); - if (tagname == NULL) - return false; - if (!strcasecmp(tagname, "INPUT")) - { - char *type = webkit_dom_element_get_attribute((void*)element, "type"); - if (!g_strcmp0(type, "text") || !g_strcmp0(type, "search")|| !g_strcmp0(type, "password")) - return true; - } - else if (!strcasecmp(tagname, "TEXTAREA")) - return true; - - return false; -}/*}}}*/ - -/* dwb_get_active_input(WebKitDOMDocument ) {{{*/ -static WebKitDOMElement * -dwb_get_active_element(WebKitDOMDocument *doc) -{ - WebKitDOMElement *ret = NULL; - WebKitDOMDocument *d = NULL; - - WebKitDOMElement *active = webkit_dom_html_document_get_active_element((void*)doc); - char *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)); - ret = dwb_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)); - ret = dwb_get_active_element(d); - } - else - ret = active; - - return ret; -}/*}}}*/ - -/* dwb_open_in_editor(void) ret: gboolean success {{{*/ -DwbStatus -dwb_open_in_editor(void) -{ - DwbStatus ret = STATUS_OK; - char **commands = NULL; - char *commandstring = NULL, *tagname, *path; - char *value = NULL; - GPid pid; - gboolean success; - - char *editor = GET_CHAR("editor"); - - if (editor == NULL) - return STATUS_ERROR; - - WebKitDOMDocument *doc = webkit_web_view_get_dom_document(CURRENT_WEBVIEW()); - WebKitDOMElement *active = dwb_get_active_element(doc); - - if (active == NULL) - return STATUS_ERROR; - - tagname = webkit_dom_element_get_tag_name(active); - if (tagname == NULL) - { - ret = STATUS_ERROR; - goto clean; - } - if (! strcasecmp(tagname, "INPUT")) - value = webkit_dom_html_input_element_get_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(active)); - else if (! strcasecmp(tagname, "TEXTAREA")) - value = webkit_dom_html_text_area_element_get_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(active)); - - if (value == NULL) - { - ret = STATUS_ERROR; - goto clean; - } - - path = util_get_temp_filename("edit"); - - commandstring = util_string_replace(editor, "dwb_uri", path); - if (commandstring == NULL) - { - ret = STATUS_ERROR; - goto clean; - } - - g_file_set_contents(path, value, -1, NULL); - commands = g_strsplit(commandstring, " ", -1); - g_free(commandstring); - - success = g_spawn_async(NULL, commands, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL); - g_strfreev(commands); - if (!success) - { - ret = STATUS_ERROR; - goto clean; - } - - EditorInfo *info = dwb_malloc(sizeof(EditorInfo)); - char *id = webkit_dom_html_element_get_id(WEBKIT_DOM_HTML_ELEMENT(active)); - if (id != NULL && strlen(id) > 0) { - info->id = id; - } - else { - info->id = NULL; - } - info->tagname = tagname; - info->element = active; - info->filename = path; - info->gl = dwb.state.fview; - g_child_watch_add(pid, (GChildWatchFunc)dwb_editor_watch, info); - -clean: - g_free(value); - - return ret; -}/*}}}*/ /* Auto insert mode {{{*/ static gboolean dwb_auto_insert(WebKitDOMElement *element) { - if (dwb_get_editable(element)) + if (dom_get_editable(element)) { dwb_change_mode(INSERT_MODE); return true; @@ -1308,7 +1082,7 @@ void dwb_check_auto_insert(GList *gl) { WebKitDOMDocument *doc = webkit_web_view_get_dom_document(WEBVIEW(gl)); - WebKitDOMElement *active = dwb_get_active_element(doc); + WebKitDOMElement *active = dom_get_active_element(doc); if (!dwb_auto_insert(active)) { WebKitDOMHTMLElement *element = webkit_dom_document_get_body(doc); @@ -1820,7 +1594,7 @@ dwb_unfocus() view_set_normal_style(dwb.state.fview); dwb_source_remove(); CLEAR_COMMAND_TEXT(); - dwb_dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); + dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); dwb.state.fview = NULL; } } /*}}}*/ @@ -3070,7 +2844,7 @@ dwb_normal_mode(gboolean clean) CLEAR_COMMAND_TEXT(); } - dwb_dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); + dom_remove_from_parent(WEBKIT_DOM_NODE(CURRENT_VIEW()->status_element), NULL); if (mode == NORMAL_MODE) webkit_web_view_set_highlight_text_matches(CURRENT_WEBVIEW(), false); @@ -465,6 +465,8 @@ enum Signal { SIG_PLUGINS_FRAME_CREATED, SIG_PLUGINS_LAST, + SIG_EDITOR_NAVIGATION, + SIG_KEY_PRESS, SIG_KEY_RELEASE, SIG_LAST, @@ -941,7 +943,6 @@ DwbStatus dwb_set_clipboard(const char *text, GdkAtom atom); char * dwb_clipboard_get_text(GdkAtom atom); void dwb_paste_primary(void); -DwbStatus dwb_open_in_editor(void); gboolean dwb_confirm(GList *gl, char *prompt, ...); void dwb_save_quickmark(const char *); @@ -970,9 +971,7 @@ DwbStatus dwb_scheme_handler(GList *gl, WebKitNetworkRequest *request); GList *dwb_get_simple_list(GList *, const char *filename); char * dwb_prompt(gboolean visibility, char *prompt, ...); -gboolean dwb_dom_remove_from_parent(WebKitDOMNode *node, GError **error); char * dwb_get_raw_data(GList *gl); -gboolean dwb_dom_add_frame_listener(WebKitWebFrame *frame, const char *signal, GCallback callback, gboolean bubble, GList *gl); void dwb_free_list(GList *list, void (*func)(void*)); void dwb_init(void); diff --git a/src/editor.c b/src/editor.c new file mode 100644 index 00000000..ccef61aa --- /dev/null +++ b/src/editor.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <string.h> +#include "dwb.h" +#include "editor.h" +#include "util.h" +#include "dom.h" + +typedef struct _EditorInfo { + char *filename; + char *id; + GList *gl; + WebKitDOMElement *element; + char *tagname; +} EditorInfo; + +/* dwb_editor_watch (GChildWatchFunc) {{{*/ +static void +editor_watch(GPid pid, int status, EditorInfo *info) +{ + gsize length; + WebKitDOMElement *e = NULL; + WebKitWebView *wv; + + char *content = util_get_file_content(info->filename, &length); + + if (content == NULL) + goto clean; + + if (!info->gl || !g_list_find(dwb.state.views, info->gl->data)) + { + if (info->id == NULL) + goto clean; + else + wv = CURRENT_WEBVIEW(); + } + else + wv = WEBVIEW(info->gl); + if (info->id != NULL) + { + WebKitDOMDocument *doc = webkit_web_view_get_dom_document(wv); + e = webkit_dom_document_get_element_by_id(doc, info->id); + + if (e == NULL && (e = info->element) == NULL ) + goto clean; + } + else if (info->element) + e = info->element; + else + goto clean; + + /* g_file_get_contents adds an additional newline */ + if (length > 0 && content[length-1] == '\n') + content[length-1] = 0; + + if (!strcasecmp(info->tagname, "INPUT")) + webkit_dom_html_input_element_set_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(e), content); + else if (!strcasecmp(info->tagname, "TEXTAREA")) + webkit_dom_html_text_area_element_set_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(e), content); + +clean: + unlink(info->filename); + g_free(info->filename); + g_free(info->id); + g_free(info); +}/*}}}*/ + +static gboolean +navigation_cb(WebKitWebView *web, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *action, + WebKitWebPolicyDecision *policy, EditorInfo *info) +{ + if (frame == webkit_web_view_get_main_frame(web)) + { + info->element = NULL; + g_free(info->id); + info->id = NULL; + g_signal_handler_disconnect(web, VIEW(info->gl)->status->signals[SIG_EDITOR_NAVIGATION]); + } + return false; +} + +/* dwb_open_in_editor(void) ret: gboolean success {{{*/ +DwbStatus +editor_open(void) +{ + DwbStatus ret = STATUS_OK; + char **commands = NULL; + char *commandstring = NULL, *tagname, *path; + char *value = NULL; + GPid pid; + gboolean success; + + char *editor = GET_CHAR("editor"); + + if (editor == NULL) + return STATUS_ERROR; + + WebKitDOMDocument *doc = webkit_web_view_get_dom_document(CURRENT_WEBVIEW()); + WebKitDOMElement *active = dom_get_active_element(doc); + + if (active == NULL) + return STATUS_ERROR; + + tagname = webkit_dom_element_get_tag_name(active); + if (tagname == NULL) + { + ret = STATUS_ERROR; + goto clean; + } + if (! strcasecmp(tagname, "INPUT")) + value = webkit_dom_html_input_element_get_value(WEBKIT_DOM_HTML_INPUT_ELEMENT(active)); + else if (! strcasecmp(tagname, "TEXTAREA")) + value = webkit_dom_html_text_area_element_get_value(WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT(active)); + + if (value == NULL) + { + ret = STATUS_ERROR; + goto clean; + } + + path = util_get_temp_filename("edit"); + + commandstring = util_string_replace(editor, "dwb_uri", path); + if (commandstring == NULL) + { + ret = STATUS_ERROR; + goto clean; + } + + g_file_set_contents(path, value, -1, NULL); + commands = g_strsplit(commandstring, " ", -1); + g_free(commandstring); + + success = g_spawn_async(NULL, commands, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL); + g_strfreev(commands); + if (!success) + { + ret = STATUS_ERROR; + goto clean; + } + + EditorInfo *info = dwb_malloc(sizeof(EditorInfo)); + char *id = webkit_dom_html_element_get_id(WEBKIT_DOM_HTML_ELEMENT(active)); + if (id != NULL && strlen(id) > 0) { + info->id = id; + } + else { + info->id = NULL; + } + info->tagname = tagname; + info->element = active; + info->filename = path; + info->gl = dwb.state.fview; + g_child_watch_add(pid, (GChildWatchFunc)editor_watch, info); + VIEW(dwb.state.fview)->status->signals[SIG_EDITOR_NAVIGATION] = + g_signal_connect(CURRENT_WEBVIEW(), "navigation-policy-decision-requested", G_CALLBACK(navigation_cb), info); +clean: + g_free(value); + + return ret; +}/*}}}*/ diff --git a/src/editor.h b/src/editor.h new file mode 100644 index 00000000..634cdeb3 --- /dev/null +++ b/src/editor.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net> + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef EDITOR_H +#define EDITOR_H + +DwbStatus editor_open(void); + +#endif diff --git a/src/plugins.c b/src/plugins.c index e71c216e..10f98f83 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -19,6 +19,7 @@ #include "dwb.h" #include "util.h" #include "view.h" +#include "dom.h" #define ALLOWED(g) (VIEW(g)->status->allowed_plugins) @@ -187,7 +188,7 @@ plugins_load_status_cb(WebKitWebView *wv, GParamSpec *p, GList *gl) void plugins_frame_load_cb(WebKitWebFrame *frame, GParamSpec *p, GList *gl) { - dwb_dom_add_frame_listener(frame, "beforeload", G_CALLBACK(plugins_before_load_cb), true, gl); + dom_add_frame_listener(frame, "beforeload", G_CALLBACK(plugins_before_load_cb), true, gl); } void diff --git a/src/scripts.c b/src/scripts.c index f3989656..b23868e9 100644 --- a/src/scripts.c +++ b/src/scripts.c @@ -97,6 +97,7 @@ static Sigmap s_sigmap[] = { { SCRIPTS_SIG_STATUS_BAR, "statusBarChange" }, { SCRIPTS_SIG_TAB_BUTTON_PRESS, "tabButtonPress" }, { SCRIPTS_SIG_CHANGE_MODE, "changeMode" }, + { SCRIPTS_SIG_EXECUTE_COMMAND, "executeCommand" }, { 0, NULL }, }; @@ -129,6 +130,7 @@ static GQuark s_ref_quark; static JSObjectRef s_init_before, s_init_after; static JSObjectRef s_constructors[CONSTRUCTOR_LAST]; static gboolean s_opt_force = false; +static JSObjectRef s_soup_session; /* Only defined once */ static JSValueRef UNDEFINED, NIL; @@ -879,6 +881,11 @@ global_get(JSContextRef ctx, JSObjectRef object, JSStringRef js_name, JSValueRef { return JSContextGetGlobalObject(ctx); } +static JSValueRef +global_get_webkit_session(JSContextRef ctx, JSObjectRef object, JSStringRef js_name, JSValueRef* exception) +{ + return s_soup_session; +} /* global_execute {{{*/ static JSValueRef global_execute(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argc, const JSValueRef argv[], JSValueRef* exc) @@ -2492,6 +2499,7 @@ create_global_object() JSStaticValue global_values[] = { { "global", global_get, NULL, kJSDefaultAttributes }, + { "session", global_get_webkit_session, NULL, kJSDefaultAttributes }, { 0, 0, 0, 0 }, }; @@ -2723,6 +2731,9 @@ create_global_object() s_download_class = JSClassCreate(&cd); s_constructors[CONSTRUCTOR_DOWNLOAD] = create_constructor(s_global_context, "Download", s_download_class, download_constructor_cb, NULL); + + s_soup_session = make_object_for_class(s_global_context, s_gobject_class, G_OBJECT(webkit_get_default_session()), false); + JSValueProtect(s_global_context, s_soup_session); }/*}}}*/ /*}}}*/ @@ -2925,6 +2936,7 @@ scripts_end() JSValueUnprotect(s_global_context, s_array_contructor); JSValueUnprotect(s_global_context, UNDEFINED); JSValueUnprotect(s_global_context, NIL); + JSValueUnprotect(s_global_context, s_soup_session); JSClassRelease(s_gobject_class); JSClassRelease(s_webview_class); JSClassRelease(s_frame_class); diff --git a/src/scripts.h b/src/scripts.h index 77a400a8..77203f25 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -47,6 +47,7 @@ enum SIGNALS { SCRIPTS_SIG_STATUS_BAR, SCRIPTS_SIG_TAB_BUTTON_PRESS, SCRIPTS_SIG_CHANGE_MODE, + SCRIPTS_SIG_EXECUTE_COMMAND, SCRIPTS_SIG_LAST, } ; @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 Stefan Bolte <portix@gmx.net> + * Copyright (c) 2010-2013 Stefan Bolte <portix@gmx.net> * * 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 @@ -32,6 +32,7 @@ #include "adblock.h" #include "js.h" #include "scripts.h" +#include "dom.h" static void view_ssl_state(GList *); static unsigned long s_click_time; @@ -421,7 +422,7 @@ view_hovering_over_link_cb(WebKitWebView *web, char *title, char *uri, GList *gl VIEW(gl)->status->hover_uri = NULL; dwb_update_uri(gl); if (! (dwb.state.bar_visible & BAR_VIS_STATUS)) - dwb_dom_remove_from_parent(WEBKIT_DOM_NODE(VIEW(gl)->hover.element), NULL); + dom_remove_from_parent(WEBKIT_DOM_NODE(VIEW(gl)->hover.element), NULL); } }/*}}}*/ diff --git a/util/settings.pre b/util/settings.pre index 3289fb29..156c3387 100644 --- a/util/settings.pre +++ b/util/settings.pre @@ -15,6 +15,7 @@ zoom-level text The default zoom level zoom-step text The zoom step # session Network & Session +accept-language text If set will be used for "Accept-Language" header in all requests addressbar-dns-lookup checkbox Whether to first perform a dns lookup for text typed in the addressbar before loading the site cookies-accept-policy select @always @never @nothirdparty The cookies to accept, affects also session cookies cookies-store-policy select @session @persistent @never The storage policy for cookies |