File indexing completed on 2024-05-12 04:58:28
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2015-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 0019 #include "scripts.h" 0020 #include "qztools.h" 0021 #include "webpage.h" 0022 0023 #include <QUrlQuery> 0024 #include <QtWebEngineWidgetsVersion> 0025 0026 QString Scripts::setupWebChannel() 0027 { 0028 QString source = QL1S("(function() {" 0029 "%1" 0030 "" 0031 "function registerExternal(e) {" 0032 " window.external = e;" 0033 " if (window.external) {" 0034 " var event = document.createEvent('Event');" 0035 " event.initEvent('_falkon_external_created', true, true);" 0036 " window._falkon_external = true;" 0037 " document.dispatchEvent(event);" 0038 " }" 0039 "}" 0040 "" 0041 "if (self !== top) {" 0042 " if (top._falkon_external)" 0043 " registerExternal(top.external);" 0044 " else" 0045 " top.document.addEventListener('_falkon_external_created', function() {" 0046 " registerExternal(top.external);" 0047 " });" 0048 " return;" 0049 "}" 0050 "" 0051 "function registerWebChannel() {" 0052 " try {" 0053 " new QWebChannel(qt.webChannelTransport, function(channel) {" 0054 " var external = channel.objects.qz_object;" 0055 " external.extra = {};" 0056 " for (var key in channel.objects) {" 0057 " if (key != 'qz_object' && key.startsWith('qz_')) {" 0058 " external.extra[key.substr(3)] = channel.objects[key];" 0059 " }" 0060 " }" 0061 " registerExternal(external);" 0062 " });" 0063 " } catch (e) {" 0064 " setTimeout(registerWebChannel, 100);" 0065 " }" 0066 "}" 0067 "registerWebChannel();" 0068 "" 0069 "})()"); 0070 0071 return source.arg(QzTools::readAllFileContents(QSL(":/qtwebchannel/qwebchannel.js"))); 0072 } 0073 0074 QString Scripts::setupFormObserver() 0075 { 0076 QString source = QL1S("(function() {" 0077 " let eFormsOld = []," 0078 " eFormsDone = [];" 0079 "" 0080 " function findUsername(inputs) {" 0081 " let usernameNames = ['user', 'name', 'login'];" 0082 "" 0083 " for (let i = 0; i < usernameNames.length; ++i) {" 0084 " for (let j = 0; j < inputs.length; ++j)" 0085 " if (inputs[j].type == 'text' && inputs[j].value.length && inputs[j].name.indexOf(usernameNames[i]) != -1)" 0086 " return inputs[j].value;" 0087 " }" 0088 "" 0089 " for (let i = 0; i < inputs.length; ++i)" 0090 " if (inputs[i].type == 'text' && inputs[i].value.length)" 0091 " return inputs[i].value;" 0092 "" 0093 " for (let i = 0; i < inputs.length; ++i)" 0094 " if (inputs[i].type == 'email' && inputs[i].value.length)" 0095 " return inputs[i].value;" 0096 "" 0097 " return '';" 0098 " }" 0099 "" 0100 " function processForm(eForm) {" 0101 " let data = '';" 0102 " let password = '';" 0103 " let inputs = eForm.getElementsByTagName('input');" 0104 "" 0105 " for (let i = 0; i < inputs.length; ++i) {" 0106 " let input = inputs[i];" 0107 " let type = input.type.toLowerCase();" 0108 " if (type != 'text' && type != 'password' && type != 'email')" 0109 " continue;" 0110 " if (!password && type == 'password')" 0111 " password = input.value;" 0112 " data += encodeURIComponent(input.name);" 0113 " data += '=';" 0114 " data += encodeURIComponent(input.value);" 0115 " data += '&';" 0116 " }" 0117 "" 0118 " if (!password)" 0119 " return;" 0120 "" 0121 " if (eFormsDone.every(e => e != eForm)) {" 0122 " eFormsDone.push(eForm);" 0123 " } else {" 0124 " return;" 0125 " }" 0126 "" 0127 " data = data.substring(0, data.length - 1);" 0128 " let url = window.location.href;" 0129 " let username = findUsername(inputs);" 0130 " external.autoFill.formSubmitted(url, username, password, data);" 0131 " }" 0132 "" 0133 " function undoForm(eForm) {" 0134 " let i = eFormsDone.indexOf(eForm);" 0135 "" 0136 " if (i >= 0) {" 0137 " eFormsDone.splice(i, 1);" 0138 " }" 0139 " }" 0140 "" 0141 " function registerForm(eForm) {" 0142 " let eInputs = eForm.getElementsByTagName('input');" 0143 "" 0144 " eForm.addEventListener('submit', () => processForm(eForm), true);" 0145 "" 0146 " for (let eInput of eInputs) {" 0147 " let type = eInput.type.toLowerCase();" 0148 " " 0149 " if (type == 'password') {" 0150 " eInput.addEventListener('blur', () => processForm(eForm), true);" 0151 " eInput.addEventListener('input', () => undoForm(eForm), true);" 0152 " eInput.addEventListener('keydown', () => event.keyCode === 13 && processForm(eForm), true);" 0153 " }" 0154 " }" 0155 " }" 0156 "" 0157 " setInterval(() => {" 0158 " try {" 0159 " let eFormsNew = Array.from(document.forms);" 0160 "" 0161 " for (let eFormNew of eFormsNew) {" 0162 " if (eFormsOld.every(e => e != eFormNew)) {" 0163 " eFormsOld.push(eFormNew);" 0164 " registerForm(eFormNew);" 0165 " }" 0166 " }" 0167 "" 0168 " } catch (e) {}" 0169 " }, 100);" 0170 "})()"); 0171 0172 return source; 0173 } 0174 0175 QString Scripts::setupWindowObject() 0176 { 0177 QString source = QL1S("(function() {" 0178 "var external = {};" 0179 "external.AddSearchProvider = function(url) {" 0180 " window.location = 'falkon:AddSearchProvider?url=' + url;" 0181 "};" 0182 "external.IsSearchProviderInstalled = function(url) {" 0183 " console.warn('NOT IMPLEMENTED: IsSearchProviderInstalled()');" 0184 " return false;" 0185 "};" 0186 "window.external = external;"); 0187 source += QL1S("})()"); 0188 0189 return source; 0190 } 0191 0192 QString Scripts::setupSpeedDial() 0193 { 0194 QString source = QzTools::readAllFileContents(QSL(":html/speeddial.user.js")); 0195 source.replace(QL1S("%JQUERY%"), QzTools::readAllFileContents(QSL(":html/jquery.js"))); 0196 source.replace(QL1S("%JQUERY-UI%"), QzTools::readAllFileContents(QSL(":html/jquery-ui.js"))); 0197 return source; 0198 } 0199 0200 QString Scripts::setCss(const QString &css) 0201 { 0202 QString source = QL1S("(function() {" 0203 "var head = document.getElementsByTagName('head')[0];" 0204 "if (!head) return;" 0205 "var css = document.createElement('style');" 0206 "css.setAttribute('type', 'text/css');" 0207 "css.appendChild(document.createTextNode('%1'));" 0208 "head.appendChild(css);" 0209 "})()"); 0210 0211 QString style = css; 0212 style.replace(QL1S("'"), QL1S("\\'")); 0213 style.replace(QL1S("\n"), QL1S("\\n")); 0214 return source.arg(style); 0215 } 0216 0217 QString Scripts::sendPostData(const QUrl &url, const QByteArray &data) 0218 { 0219 QString source = QL1S("(function() {" 0220 "var form = document.createElement('form');" 0221 "form.setAttribute('method', 'POST');" 0222 "form.setAttribute('action', '%1');" 0223 "var val;" 0224 "%2" 0225 "form.submit();" 0226 "})()"); 0227 0228 QString valueSource = QL1S("val = document.createElement('input');" 0229 "val.setAttribute('type', 'hidden');" 0230 "val.setAttribute('name', '%1');" 0231 "val.setAttribute('value', '%2');" 0232 "form.appendChild(val);"); 0233 0234 QString values; 0235 QUrlQuery query(QString::fromUtf8(data)); 0236 0237 const auto &queryItems = query.queryItems(QUrl::FullyDecoded); 0238 for (int i = 0; i < queryItems.size(); ++i) { 0239 const auto &pair = queryItems[i]; 0240 QString value = pair.first; 0241 QString key = pair.second; 0242 value.replace(QL1S("'"), QL1S("\\'")); 0243 key.replace(QL1S("'"), QL1S("\\'")); 0244 values.append(valueSource.arg(value, key)); 0245 } 0246 0247 return source.arg(url.toString(), values); 0248 } 0249 0250 QString Scripts::completeFormData(const QByteArray &data) 0251 { 0252 QString source = QL1S("(function() {" 0253 "var data = '%1'.split('&');" 0254 "var inputs = document.getElementsByTagName('input');" 0255 "" 0256 "for (var i = 0; i < data.length; ++i) {" 0257 " var pair = data[i].split('=');" 0258 " if (pair.length != 2)" 0259 " continue;" 0260 " var key = decodeURIComponent(pair[0]);" 0261 " var val = decodeURIComponent(pair[1]);" 0262 " for (var j = 0; j < inputs.length; ++j) {" 0263 " var input = inputs[j];" 0264 " var type = input.type.toLowerCase();" 0265 " if (type != 'text' && type != 'password' && type != 'email')" 0266 " continue;" 0267 " if (input.name == key && input.name != '') {" 0268 " input.value = val;" 0269 " input.dispatchEvent(new Event('change'));" 0270 " }" 0271 " }" 0272 "}" 0273 "" 0274 "})()"); 0275 0276 QString d = QString::fromUtf8(data); 0277 d.replace(QL1S("'"), QL1S("\\'")); 0278 return source.arg(d); 0279 } 0280 0281 QString Scripts::getOpenSearchLinks() 0282 { 0283 QString source = QL1S("(function() {" 0284 "var out = [];" 0285 "var links = document.getElementsByTagName('link');" 0286 "for (var i = 0; i < links.length; ++i) {" 0287 " var e = links[i];" 0288 " if (e.type == 'application/opensearchdescription+xml') {" 0289 " out.push({" 0290 " url: e.href," 0291 " title: e.title" 0292 " });" 0293 " }" 0294 "}" 0295 "return out;" 0296 "})()"); 0297 0298 return source; 0299 } 0300 0301 QString Scripts::getAllImages() 0302 { 0303 QString source = QL1S("(function() {" 0304 "var out = [];" 0305 "var imgs = document.getElementsByTagName('img');" 0306 "for (var i = 0; i < imgs.length; ++i) {" 0307 " var e = imgs[i];" 0308 " out.push({" 0309 " src: e.src," 0310 " alt: e.alt" 0311 " });" 0312 "}" 0313 "return out;" 0314 "})()"); 0315 0316 return source; 0317 } 0318 0319 QString Scripts::getAllMetaAttributes() 0320 { 0321 QString source = QL1S("(function() {" 0322 "var out = [];" 0323 "var meta = document.getElementsByTagName('meta');" 0324 "for (var i = 0; i < meta.length; ++i) {" 0325 " var e = meta[i];" 0326 " out.push({" 0327 " name: e.getAttribute('name')," 0328 " content: e.getAttribute('content')," 0329 " httpequiv: e.getAttribute('http-equiv')" 0330 " });" 0331 "}" 0332 "return out;" 0333 "})()"); 0334 0335 return source; 0336 } 0337 0338 QString Scripts::getFormData(const QPointF &pos) 0339 { 0340 QString source = QL1S("(function() {" 0341 "var e = document.elementFromPoint(%1, %2);" 0342 "if (!e || e.tagName.toLowerCase() != 'input')" 0343 " return;" 0344 "var fe = e.parentElement;" 0345 "while (fe) {" 0346 " if (fe.tagName.toLowerCase() == 'form')" 0347 " break;" 0348 " fe = fe.parentElement;" 0349 "}" 0350 "if (!fe)" 0351 " return;" 0352 "var res = {" 0353 " method: fe.method.toLowerCase()," 0354 " action: fe.action," 0355 " inputName: e.name," 0356 " inputs: []," 0357 "};" 0358 "for (var i = 0; i < fe.length; ++i) {" 0359 " var input = fe.elements[i];" 0360 " res.inputs.push([input.name, input.value]);" 0361 "}" 0362 "return res;" 0363 "})()"); 0364 0365 return source.arg(pos.x()).arg(pos.y()); 0366 } 0367 0368 QString Scripts::scrollToAnchor(const QString &anchor) 0369 { 0370 QString source = QL1S("(function() {" 0371 "var e = document.getElementById(\"%1\");" 0372 "if (!e) {" 0373 " var els = document.querySelectorAll(\"[name='%1']\");" 0374 " if (els.length)" 0375 " e = els[0];" 0376 "}" 0377 "if (e)" 0378 " e.scrollIntoView();" 0379 "})()"); 0380 0381 return source.arg(anchor); 0382 }