File: noscript.lua

package info (click to toggle)
luakit 1%3A2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,844 kB
  • sloc: ansic: 12,519; makefile: 140; ruby: 79; sh: 48
file content (228 lines) | stat: -rw-r--r-- 7,115 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
--- NoScript plugin for luakit.
--
-- This module provides a method of restricting web page access to JavaScript
-- and plugins.
--
-- This module provides keybindings for enabling/disabling either plugins or
-- JavaScript for the current web page, as well as a status bar widget that
-- indicates whether JavaScript is enabled for the current web page.
--
-- @module noscript
-- @copyright 2011 Mason Larobina <mason.larobina@gmail.com>

local window = require("window")
local webview = require("webview")
local modes = require("modes")
local add_binds = modes.add_binds
local lousy = require("lousy")
local sql_escape = lousy.util.sql_escape
local theme = require("theme")

local _M = {}

--- Whether JavaScript should be enabled by default.
-- @type boolean
-- @readwrite
-- @default `true`
_M.enable_scripts = true

--- Whether plugins should be enabled by default.
-- @type boolean
-- @readwrite
-- @default `true`
_M.enable_plugins = true

local create_table = [[
CREATE TABLE IF NOT EXISTS by_domain (
    id INTEGER PRIMARY KEY,
    domain TEXT,
    enable_scripts INTEGER,
    enable_plugins INTEGER
);]]

local db = sqlite3{ filename = luakit.data_dir .. "/noscript.db" }
db:exec("PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;")
db:exec(create_table)

local function btoi(bool) return bool and 1 or 0    end
local function itob(int)  return tonumber(int) ~= 0 end

local function get_domain(uri)
    uri = lousy.uri.parse(uri)
    -- uri parsing will fail on some URIs, e.g. "about:blank"
    return (uri and uri.host) and string.lower(uri.host) or nil
end

local function match_domain(domain)
    local rows = db:exec(string.format("SELECT * FROM by_domain "
        .. "WHERE domain == %s;", sql_escape(domain)))
    if rows[1] then return rows[1] end
end

local function update(id, field, value)
    db:exec(string.format("UPDATE by_domain SET %s = %d WHERE id == %d;",
        field, btoi(value), id))
end

local function insert(domain, enable_scripts, enable_plugins)
    db:exec(string.format("INSERT INTO by_domain VALUES (NULL, %s, %d, %d);",
        sql_escape(domain), btoi(enable_scripts), btoi(enable_plugins)))
end

function webview.methods.toggle_scripts(view, w)
    local domain = get_domain(view.uri)
    local enable_scripts = _M.enable_scripts
    local row = match_domain(domain)

    if row then
        enable_scripts = itob(row.enable_scripts)
        update(row.id, "enable_scripts", not enable_scripts)
    else
        insert(domain, not enable_scripts, _M.enable_plugins)
    end

    w:notify(string.format("%sabled scripts for domain: %s",
        enable_scripts and "Dis" or "En", domain))
end

function webview.methods.toggle_plugins(view, w)
    local domain = get_domain(view.uri)
    local enable_plugins = _M.enable_plugins
    local row = match_domain(domain)

    if row then
        enable_plugins = itob(row.enable_plugins)
        update(row.id, "enable_plugins", not enable_plugins)
    else
        insert(domain, _M.enable_scripts, not enable_plugins)
    end

    w:notify(string.format("%sabled plugins for domain: %s",
        enable_plugins and "Dis" or "En", domain))
end

function webview.methods.toggle_remove(view, w)
    local domain = get_domain(view.uri)
    db:exec(string.format("DELETE FROM by_domain WHERE domain == %s;",
        sql_escape(domain)))
    w:notify("Removed rules for domain: " .. domain)
end

local function string_starts(a, b)
    return string.sub(a, 1, string.len(b)) == b
end

local function lookup_domain(uri)
    if not uri then uri = "" end
    local enable_scripts, enable_plugins = _M.enable_scripts, _M.enable_plugins
    local domain = get_domain(uri)
    -- Enable everything for local pages
    if string_starts(uri, "file://") then return true, true, "file://" end

    -- Look up this domain and all parent domains, returning the first match
    -- E.g. querying a.b.com will lookup a.b.com, then b.com, then com
    while domain do
        local row = match_domain(domain)
        if row then
            return itob(row.enable_scripts), itob(row.enable_plugins), row.domain
        end
        domain = string.match(domain, "%.(.+)")
    end

    return enable_scripts, enable_plugins, nil
end

-- NoScript indicator

local view_noscript_state = setmetatable({}, { __mode = "k" })

local function noscript_indicator_update(v)
    local vns = view_noscript_state[v]
    local w = webview.window(v)
    if not vns or not w then return end

    local ns = w.sbar.r.noscript
    local es, matched_domain = vns.enable_scripts, vns.enable_scripts_domain
    local state = es and "enabled" or "disabled"

    if es then
        ns.text = "S" or "<s>S</s>"
        ns.fg = theme.trust_fg
    else
        ns.text = "<s>S</s>"
        ns.fg = theme.notrust_fg
    end

    if matched_domain == "override" then
        ns.tooltip = "JavaScript " .. state
    elseif matched_domain then
        ns.tooltip = "JavaScript " .. state .. ": URI matched domain '" .. matched_domain .. "'"
    else
        ns.tooltip = "JavaScript " .. state .. ": default setting"
    end
end

local noscript_ss = stylesheet{ source = [===[noscript { display: none !important; }]===] }

window.add_signal("init", function (w)
    local r = w.sbar.r
    r.noscript = widget{type="label"}
    r.layout:pack(r.noscript)
    r.layout:reorder(r.noscript, 1)
    r.noscript.font = theme.font
end)

local update_webview_blocking = function (v)
    local es = v:emit_signal("enable-scripts")
    local ep = v:emit_signal("enable-plugins")
    local vns = {
        enable_scripts_domain = es and "override" or nil,
        enable_plugins_domain = ep and "override" or nil,
    }
    if es == nil or ep == nil then
        local s, p, matched_domain = lookup_domain(v.uri)
        if es == nil then es = s; vns.enable_scripts_domain = matched_domain end
        if ep == nil then ep = p; vns.enable_plugins_domain = matched_domain end
    end
    vns.enable_scripts = es
    vns.enable_plugins = ep
    v.enable_scripts = es
    v.enable_plugins = ep
    -- Update indicator
    view_noscript_state[v] = vns
    noscript_indicator_update(v)
    -- Workaround for https://github.com/aidanholm/luakit/issues/250
    v.stylesheets[noscript_ss] = es
end

webview.add_signal("init", function (view)
    -- Update on new resource load
    view:add_signal("navigation-request", function (v, _, _)
        update_webview_blocking(v)
    end)

    -- Update on history navigation
    view:add_signal("load-status", function (v, status)
        if status == "committed" then
            update_webview_blocking(v)
        end
    end)

    view:add_signal("switched-page", function (v)
        noscript_indicator_update(v)
    end)
end)


add_binds("normal", {
    { "^,ts$", "Enable/disable JavaScript for the current domain.",
        function (w) w:toggle_scripts() end },
    { "^,tp$", "Enable/disable plugins for the current domain.",
        function (w) w:toggle_plugins() end },
    { "^,tr$", "Remove all previously added rules for the current domain.",
        function (w) w:toggle_remove()  end },
})

return _M

-- vim: et:sw=4:ts=8:sts=4:tw=80