diff options
Diffstat (limited to 'examples/data')
-rw-r--r-- | examples/data/per-site-settings | 3 | ||||
-rw-r--r-- | examples/data/plugins/cookies.py | 176 | ||||
-rw-r--r-- | examples/data/plugins/downloads.py | 69 | ||||
-rwxr-xr-x | examples/data/scripts/auth.py | 2 | ||||
-rwxr-xr-x | examples/data/scripts/download.sh | 35 | ||||
-rwxr-xr-x | examples/data/scripts/follow.sh | 2 | ||||
-rwxr-xr-x | examples/data/scripts/formfiller.sh | 2 | ||||
-rwxr-xr-x | examples/data/scripts/load_cookies.sh | 20 | ||||
-rwxr-xr-x | examples/data/scripts/per-site-settings.py | 117 | ||||
-rw-r--r-- | examples/data/scripts/pipermail.js | 71 | ||||
-rwxr-xr-x | examples/data/scripts/scheme.py | 2 | ||||
-rwxr-xr-x | examples/data/scripts/session.sh | 6 | ||||
-rwxr-xr-x | examples/data/scripts/userscript.sh | 38 | ||||
-rwxr-xr-x | examples/data/scripts/userscripts.sh | 8 | ||||
-rw-r--r-- | examples/data/scripts/util/dmenu.sh | 12 | ||||
-rwxr-xr-x | examples/data/scripts/uzbl-tabbed | 4 |
16 files changed, 487 insertions, 80 deletions
diff --git a/examples/data/per-site-settings b/examples/data/per-site-settings new file mode 100644 index 0000000..78bade4 --- /dev/null +++ b/examples/data/per-site-settings @@ -0,0 +1,3 @@ +.* + .*/\d+-\w+/(thread|subject|author|date).html + script @data_home/uzbl/scripts/pipermail.js diff --git a/examples/data/plugins/cookies.py b/examples/data/plugins/cookies.py new file mode 100644 index 0000000..c9fe2c3 --- /dev/null +++ b/examples/data/plugins/cookies.py @@ -0,0 +1,176 @@ +""" Basic cookie manager + forwards cookies to all other instances connected to the event manager""" + +from collections import defaultdict +import os, re + +# these are symbolic names for the components of the cookie tuple +symbolic = {'domain': 0, 'path':1, 'name':2, 'value':3, 'scheme':4, 'expires':5} + +_splitquoted = re.compile("( |\\\".*?\\\"|'.*?')") +def splitquoted(text): + return [str(p.strip('\'"')) for p in _splitquoted.split(text) if p.strip()] + +# allows for partial cookies +# ? allow wildcard in key +def match(key, cookie): + for k,c in zip(key,cookie): + if k != c: + return False + return True + +class NullStore(object): + def add_cookie(self, rawcookie, cookie): + pass + + def delete_cookie(self, rkey, key): + pass + +class ListStore(list): + def add_cookie(self, rawcookie, cookie): + self.append(rawcookie) + + def delete_cookie(self, rkey, key): + self[:] = [x for x in self if not match(key, splitquoted(x))] + +class TextStore(object): + def __init__(self, filename): + self.filename = filename + + def as_event(self, cookie): + if cookie[0].startswith("#HttpOnly_"): + domain = cookie[0][len("#HttpOnly_"):] + elif cookie[0].startswith('#'): + return None + else: + domain = cookie[0] + return (domain, + cookie[2], + cookie[5], + cookie[6], + 'https' if cookie[3] == 'TRUE' else 'http', + cookie[4]) + + def as_file(self, cookie): + return (cookie[0], + 'TRUE' if cookie[0].startswith('.') else 'FALSE', + cookie[1], + 'TRUE' if cookie[4] == 'https' else 'FALSE', + cookie[5], + cookie[2], + cookie[3]) + + def add_cookie(self, rawcookie, cookie): + assert len(cookie) == 6 + + # delete equal cookies (ignoring expire time, value and secure flag) + self.delete_cookie(None, cookie[:-3]) + + first = not os.path.exists(self.filename) + with open(self.filename, 'a') as f: + if first: + print >> f, "# HTTP Cookie File" + print >> f, '\t'.join(self.as_file(cookie)) + + def delete_cookie(self, rkey, key): + if not os.path.exists(self.filename): + return + + # read all cookies + with open(self.filename, 'r') as f: + cookies = f.readlines() + + # write those that don't match the cookie to delete + with open(self.filename, 'w') as f: + for l in cookies: + c = self.as_event(l.split('\t')) + if c is None or not match(key, c): + print >> f, l, + +xdg_data_home = os.environ.get('XDG_DATA_HOME', os.path.join(os.environ['HOME'], '.local/share')) +DefaultStore = TextStore(os.path.join(xdg_data_home, 'uzbl/cookies.txt')) +SessionStore = TextStore(os.path.join(xdg_data_home, 'uzbl/session-cookies.txt')) + +def match_list(_list, cookie): + for component, match in _list: + if match(cookie[component]) is not None: + return True + return False + +# accept a cookie only when: +# a. there is no whitelist and the cookie is in the blacklist +# b. the cookie is in the whitelist and not in the blacklist +def accept_cookie(uzbl, cookie): + if uzbl.cookie_whitelist: + if match_list(uzbl.cookie_whitelist, cookie): + return not match_list(uzbl.cookie_blacklist, cookie) + return False + + return not match_list(uzbl.cookie_blacklist, cookie) + +def expires_with_session(uzbl, cookie): + return cookie[5] == '' + +def get_recipents(uzbl): + """ get a list of Uzbl instances to send the cookie too. """ + # This could be a lot more interesting + return [u for u in uzbl.parent.uzbls.values() if u is not uzbl] + +def get_store(uzbl, session=False): + if session: + return SessionStore + return DefaultStore + +def add_cookie(uzbl, cookie): + splitted = splitquoted(cookie) + if accept_cookie(uzbl, splitted): + for u in get_recipents(uzbl): + u.send('add_cookie %s' % cookie) + + get_store(uzbl, expires_with_session(uzbl, splitted)).add_cookie(cookie, splitted) + else: + logger.debug('cookie %r is blacklisted' % splitted) + uzbl.send('delete_cookie %s' % cookie) + +def delete_cookie(uzbl, cookie): + for u in get_recipents(uzbl): + u.send('delete_cookie %s' % cookie) + + splitted = splitquoted(cookie) + if len(splitted) == 6: + get_store(uzbl, expires_with_session(uzbl, splitted)).delete_cookie(cookie, splitted) + else: + for store in set([get_store(uzbl, session) for session in (True, False)]): + store.delete_cookie(cookie, splitted) + +# add a cookie matcher to a whitelist or a blacklist. +# a matcher is a (component, re) tuple that matches a cookie when the +# "component" part of the cookie matches the regular expression "re". +# "component" is one of the keys defined in the variable "symbolic" above, +# or the index of a component of a cookie tuple. +def add_cookie_matcher(_list, arg): + component, regexp = splitquoted(arg) + try: + component = symbolic[component] + except KeyError: + component = int(component) + assert component <= 5 + _list.append((component, re.compile(regexp).search)) + +def blacklist(uzbl, arg): + add_cookie_matcher(uzbl.cookie_blacklist, arg) + +def whitelist(uzbl, arg): + add_cookie_matcher(uzbl.cookie_whitelist, arg) + +def init(uzbl): + connect_dict(uzbl, { + 'ADD_COOKIE': add_cookie, + 'DELETE_COOKIE': delete_cookie, + 'BLACKLIST_COOKIE': blacklist, + 'WHITELIST_COOKIE': whitelist + }) + export_dict(uzbl, { + 'cookie_blacklist' : [], + 'cookie_whitelist' : [] + }) diff --git a/examples/data/plugins/downloads.py b/examples/data/plugins/downloads.py new file mode 100644 index 0000000..7bf32d7 --- /dev/null +++ b/examples/data/plugins/downloads.py @@ -0,0 +1,69 @@ +# this plugin does a very simple display of download progress. to use it, add +# @downloads to your status_format. + +import os +ACTIVE_DOWNLOADS = {} + +# after a download's status has changed this is called to update the status bar +def update_download_section(uzbl): + global ACTIVE_DOWNLOADS + + if len(ACTIVE_DOWNLOADS): + # add a newline before we list downloads + result = ' downloads:' + for path in ACTIVE_DOWNLOADS: + # add each download + fn = os.path.basename(path) + progress, = ACTIVE_DOWNLOADS[path] + + dl = " %s (%d%%)" % (fn, progress * 100) + + # replace entities to make sure we don't break our markup + # (this could be done with an @[]@ expansion in uzbl, but then we + # can't use the above to make a new line) + dl = dl.replace("&", "&").replace("<", "<") + result += dl + else: + result = '' + + # and the result gets saved to an uzbl variable that can be used in + # status_format + if uzbl.config.get('downloads', '') != result: + uzbl.config['downloads'] = result + +def download_started(uzbl, destination_path): + # add to the list of active downloads + global ACTIVE_DOWNLOADS + ACTIVE_DOWNLOADS[destination_path] = (0.0,) + + # update the progress + update_download_section(uzbl) + +def download_progress(uzbl, args): + # parse the arguments + s = args.rindex(' ') + destination_path = args[:s] + progress = float(args[s+1:]) + + # update the progress + global ACTIVE_DOWNLOADS + ACTIVE_DOWNLOADS[destination_path] = (progress,) + + # update the status bar variable + update_download_section(uzbl) + +def download_complete(uzbl, destination_path): + # remove from the list of active downloads + global ACTIVE_DOWNLOADS + del ACTIVE_DOWNLOADS[destination_path] + + # update the status bar variable + update_download_section(uzbl) + +# plugin init hook +def init(uzbl): + connect_dict(uzbl, { + 'DOWNLOAD_STARTED': download_started, + 'DOWNLOAD_PROGRESS': download_progress, + 'DOWNLOAD_COMPLETE': download_complete, + }) diff --git a/examples/data/scripts/auth.py b/examples/data/scripts/auth.py index 9c1b4fc..592a2c6 100755 --- a/examples/data/scripts/auth.py +++ b/examples/data/scripts/auth.py @@ -46,7 +46,7 @@ def getText(authInfo, authHost, authRealm): return rv, output if __name__ == '__main__': - rv, output = getText(sys.argv[8], sys.argv[9], sys.argv[10]) + rv, output = getText(sys.argv[1], sys.argv[2], sys.argv[3]) if (rv == gtk.RESPONSE_OK): print output; else: diff --git a/examples/data/scripts/download.sh b/examples/data/scripts/download.sh index b378a85..c410ad2 100755 --- a/examples/data/scripts/download.sh +++ b/examples/data/scripts/download.sh @@ -1,26 +1,25 @@ #!/bin/sh -# just an example of how you could handle your downloads -# try some pattern matching on the uri to determine what we should do +# +# uzbl's example configuration sets this script up as its download_handler. +# when uzbl starts a download it runs this script. +# if the script prints a file path to stdout, uzbl will save the download to +# that path. +# if nothing is printed to stdout, the download will be cancelled. -shift 7 . $UZBL_UTIL_DIR/uzbl-dir.sh -# Some sites block the default wget --user-agent.. -GET="wget --user-agent=Firefox --content-disposition --load-cookies=$UZBL_COOKIE_FILE" +# the URL that is being downloaded +uri=$1 -url="$1" +# a filename suggested by the server or based on the URL +suggested_filename=${2:-$(echo "$uri" | sed 's/\W/-/g')} -http_proxy="$2" -export http_proxy +# the mimetype of the file being downloaded +content_type=$3 -if [ -z "$url" ]; then - echo "you must supply a url! ($url)" - exit 1 -fi +# the size of the downloaded file in bytes. this is not always accurate, since +# the server might not have sent a size with its response headers. +total_size=$4 -# only changes the dir for the $get sub process -if echo "$url" | grep -E '.*\.torrent' >/dev/null; then - ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url") -else - ( cd "$UZBL_DOWNLOAD_DIR"; $GET "$url") -fi +# just save the file to the default directory with the suggested name +echo $UZBL_DOWNLOAD_DIR/$suggested_filename diff --git a/examples/data/scripts/follow.sh b/examples/data/scripts/follow.sh index 2d666a2..d1560bf 100755 --- a/examples/data/scripts/follow.sh +++ b/examples/data/scripts/follow.sh @@ -3,8 +3,6 @@ # This script is just a wrapper around follow.js that lets us change uzbl's mode # after a link is selected. -shift 7 - # 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 diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 6e04573..c6822e6 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -53,8 +53,6 @@ MODELINE="> vim:ft=formfiller" [ -d "$(dirname $UZBL_FORMS_DIR)" ] || exit 1 [ -d $UZBL_FORMS_DIR ] || mkdir $UZBL_FORMS_DIR || exit 1 -shift 7 - action=$1 domain=$(echo $UZBL_URI | sed 's/\(http\|https\):\/\/\([^\/]\+\)\/.*/\2/') diff --git a/examples/data/scripts/load_cookies.sh b/examples/data/scripts/load_cookies.sh new file mode 100755 index 0000000..17ec2ad --- /dev/null +++ b/examples/data/scripts/load_cookies.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +if [ "$1" != "" ]; then + cookie_file=$1 +else + cookie_file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/cookies.txt +fi + +awk -F \\t ' +BEGIN { + scheme["TRUE"] = "https"; + scheme["FALSE"] = "http"; +} +$0 ~ /^#HttpOnly_/ { +printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", substr($1,length("#HttpOnly_"),length($1)), $3, $6, $7, scheme[$4], $5) +} +$0 !~ /^#/ { +printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", $1, $3, $6, $7, scheme[$4], $5) +} +' $cookie_file diff --git a/examples/data/scripts/per-site-settings.py b/examples/data/scripts/per-site-settings.py new file mode 100755 index 0000000..89df4e6 --- /dev/null +++ b/examples/data/scripts/per-site-settings.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +# Per-site settings plugin + +# Example configuration usage: +# +# @on_event LOAD_COMMIT spawn @scripts_dir/per-site-settings.py @data_home/uzbl/per-site-settings + +# Format of the settings file: +# +# <url> +# <path> +# <command> +# +# - url +# May either be a regex, or literal. If literal, it will block any +# subdomains as well. +# - path +# May either be a regex, or literal. If literal, it will block any +# decendent paths as well. +# - command +# Given to uzbl verbatim. +# +# Matches are attempted on a literal match first. +# +# Any of the specifications can be repeated and acts as a fall-through to the +# next level. Make sure indentation lines up locally. Any indentation addition +# is considered as a fall through to the next level and any decrease is +# considered a pop back (zero is always urls). This works because it's only 3 +# deep. Four and we'd have to keep track of things. + +import os +import re +import socket +import stat +import subprocess +import tempfile +import urlparse +import sys + + +def match_url(url, patt): + return url.endswith(patt) or re.match(patt, url) + + +def match_path(path, patt): + return path.startswith(patt) or re.match(patt, path) + + +def grep_url(url, path, fin): + entries = [] + cur_indent = 0 + passing = [False, False] + # 0 == url + # 1 == path + # 2 == command + state = 0 + for line in fin: + raw = line.strip() + + indent = len(line) - len(raw) - 1 + if not indent: + # Reset state + passing = [False, False] + state = 0 + else: + # previous level + if indent < cur_indent: + if state == 1: + passing[0] = False + elif state == 2: + passing[1] = False + state -= 1 + # next level + elif cur_indent < indent: + state += 1 + + # parse the line + if state == 0: + if not passing[0] and match_url(url, raw): + passing[0] = True + elif state == 1 and passing[0]: + if not passing[1] and match_path(path, raw): + passing[1] = True + elif state == 2 and passing[1]: + entries.append(raw) + + cur_indent = indent + + return entries + + +def write_to_socket(commands, sockpath): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(sockpath) + for command in commands: + sock.send(command) + sock.close() + + +if __name__ == '__main__': + sockpath = os.environ['UZBL_SOCKET'] + url = urlparse.urlparse(os.environ['UZBL_URI']) + filepath = sys.argv[1] + + mode = os.stat(filepath)[stat.ST_MODE] + + if mode & stat.S_IEXEC: + fin = tempfile.TemporaryFile() + subprocess.Popen([filepath], stdout=fin).wait() + else: + fin = open(filepath, 'r') + + commands = grep_url(url.hostname, url.path, fin) + + fin.close() + + write_to_socket(commands, sockpath) diff --git a/examples/data/scripts/pipermail.js b/examples/data/scripts/pipermail.js new file mode 100644 index 0000000..5ec4aa4 --- /dev/null +++ b/examples/data/scripts/pipermail.js @@ -0,0 +1,71 @@ +// this is a userscript inspired by "Pipermail Navigation Links" by Michael +// Stone <http://userscripts.org/scripts/show/3174>. + +// it adds previous month/next month navigation links in pipermail mailing +// list archives. + +// we wrap the whole thing in a function (that gets called immediately) so +// that this script doesn't interfere with any javascript in the page. +(function() { + +// figure out what page we're looking at right now +var urlparts = document.location.toString().split("/"); +var currView = urlparts[urlparts.length-1].split("#")[0]; +var currDate = urlparts[urlparts.length-2].split("-"); + +// figure out the URLs to the next month and previous month +var months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', + 'August', 'September', 'October', 'November', 'December' ]; + +var thisMonth = currDate[1]; +var prevMonth; +var nextMonth; + +var thisYear = currDate[0]; +var prevYear = thisYear; +var nextYear = thisYear; + +if(thisMonth == 'January') { + prevMonth = "December"; + nextMonth = "February"; + prevYear = parseInt(thisYear) - 1; +} else if(thisMonth == 'December') { + prevMonth = "November"; + nextMonth = "January"; + nextYear = parseInt(thisYear) + 1; +} else { + var monthNum = months.indexOf(thisMonth); + prevMonth = months[monthNum - 1]; + nextMonth = months[monthNum + 1]; +} + +var prevHref = "../" + prevYear + "-" + prevMonth + "/" + currView; +var nextHref = "../" + nextYear + "-" + nextMonth + "/" + currView; + +// find the navigation header and footer +var selector = "a[href='date.html#start']"; + +// if we're on a "date" page then the date link isn't displayed +if(currView == "date.html") + selector = "a[href='author.html#start']"; + +var navLinks = document.querySelectorAll(selector); + +// append the prev/next links to the navigation header and footer +for(var i = 0; i < navLinks.length; i++) { + var victim = navLinks[i].parentNode; + + var prevEl = document.createElement("a"); + prevEl.textContent = "[ prev month ]"; + prevEl.href = prevHref; + + var nextEl = document.createElement("a"); + nextEl.textContent = "[ next month ]"; + nextEl.href = nextHref; + + victim.appendChild(prevEl); + victim.appendChild(document.createTextNode(" ")); + victim.appendChild(nextEl); +} + +})(); diff --git a/examples/data/scripts/scheme.py b/examples/data/scripts/scheme.py index 0916466..4b0b7ca 100755 --- a/examples/data/scripts/scheme.py +++ b/examples/data/scripts/scheme.py @@ -13,7 +13,7 @@ def detach_open(cmd): print 'USED' if __name__ == '__main__': - uri = sys.argv[8] + uri = sys.argv[1] u = urlparse.urlparse(uri) if u.scheme == 'mailto': detach_open(['xterm', '-e', 'mail', u.path]) diff --git a/examples/data/scripts/session.sh b/examples/data/scripts/session.sh index 36e0c19..ee09cf2 100755 --- a/examples/data/scripts/session.sh +++ b/examples/data/scripts/session.sh @@ -30,12 +30,6 @@ fi UZBL="uzbl-browser -c $UZBL_CONFIG_FILE" # add custom flags and whatever here. -if [ $# -gt 1 ]; then - # this script is being run from uzbl, rather than standalone - # discard the uzbl arguments - shift 7 -fi - scriptfile=$(readlink -f $0) # this script act="$1" diff --git a/examples/data/scripts/userscript.sh b/examples/data/scripts/userscript.sh deleted file mode 100755 index fd95fdc..0000000 --- a/examples/data/scripts/userscript.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -if [ $# = "3" ] -then - UZBL_FIFO=$1 - UZBL_URI=$2 - SCRIPT=$3 -else - SCRIPT=$8 -fi - -# Extract metadata chunk -META="`sed -ne '/^\s*\/\/\s*==UserScript==\s*$/,/^\s*\/\/\s*==\/UserScript==\s*$/p' "$SCRIPT"`" -SHOULD_RUN=false # Assume this script will not be included -# Loop over all include rules -for INCLUDE in `echo "$META" | grep "^\s*\/\/\s*@include"`; do - # Munge into grep pattern - INCLUDE="`echo "$INCLUDE" | sed -e 's/^\s*\/\/\s*@include\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`" - if echo "$UZBL_URI" | grep -x "$INCLUDE"; then - SHOULD_RUN=true - break - fi -done - -# Loop over all exclude rules -for EXCLUDE in `echo "$META" | grep "^\s*\/\/\s*@exclude"`; do - # Munge into grep pattern - EXCLUDE="`echo "$EXCLUDE" | sed -e 's/^\s*\/\/\s*@exclude\s*//' -e 's/\./\\\\./g' -e 's/\*/.*/g' -e 's/[\r\n]//g'`" - if echo "$UZBL_URI" | grep -x "$EXCLUDE"; then - SHOULD_RUN=false - break - fi -done - -# Run the script -if [ $SHOULD_RUN = true ]; then - echo "script '$SCRIPT'" > "$UZBL_FIFO" -fi diff --git a/examples/data/scripts/userscripts.sh b/examples/data/scripts/userscripts.sh deleted file mode 100755 index 4f76c90..0000000 --- a/examples/data/scripts/userscripts.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -scripts_dir="$XDG_DATA_HOME/uzbl/userscripts" - -for SCRIPT in $(grep -rlx "\s*//\s*==UserScript==\s*" "$scripts_dir") -do - $XDG_DATA_HOME/uzbl/scripts/userscript.sh "$UZBL_FIFO" "$UZBL_URI" "$SCRIPT" -done diff --git a/examples/data/scripts/util/dmenu.sh b/examples/data/scripts/util/dmenu.sh index da61cae..354d7d1 100644 --- a/examples/data/scripts/util/dmenu.sh +++ b/examples/data/scripts/util/dmenu.sh @@ -60,10 +60,16 @@ fi if dmenu --help 2>&1 | grep -q '\[-xs\]'; then DMENU_XMMS_ARGS="-xs" DMENU_HAS_XMMS=1 +fi - if echo $DMENU_OPTIONS | grep -q -w 'xmms'; then - DMENU_ARGS="$DMENU_ARGS $DMENU_XMMS_ARGS" - fi +# Detect the tok patch +if dmenu --help 2>&1 | grep -q '\[-t\]'; then + DMENU_XMMS_ARGS="-t" + DMENU_HAS_XMMS=1 +fi + +if echo $DMENU_OPTIONS | grep -q -w 'xmms'; then + DMENU_ARGS="$DMENU_ARGS $DMENU_XMMS_ARGS" fi # Detect the vertical patch diff --git a/examples/data/scripts/uzbl-tabbed b/examples/data/scripts/uzbl-tabbed index 0086c04..1d64436 100755 --- a/examples/data/scripts/uzbl-tabbed +++ b/examples/data/scripts/uzbl-tabbed @@ -452,7 +452,7 @@ class UzblInstance: type, args = args.split(" ", 1) if type == "TITLE_CHANGED": self.title = args.strip() - self.title_changed() + self.title_changed(False) elif type == "VARIABLE_SET": var, _, val = args.split(" ", 2) @@ -485,6 +485,8 @@ class UzblInstance: if var == "uri": self.uri = val.strip() self.parent.update_tablist() + elif type == "LOAD_COMMIT": + self.uri = args elif type == "NEW_TAB": self.parent.new_tab(args) elif type == "NEW_BG_TAB": |