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 }