/* ============================================================ * Falkon - Qt web browser * Copyright (C) 2015-2018 David Rosca * * 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, see . * ============================================================ */ #include "scripts.h" #include "qztools.h" #include "webpage.h" #include QString Scripts::setupWebChannel(quint32 worldId) { QString source = QL1S("// ==UserScript==\n" "%1\n" "// ==/UserScript==\n\n" "(function() {" "%2" "" "function registerExternal(e) {" " window.external = e;" " if (window.external) {" " var event = document.createEvent('Event');" " event.initEvent('_falkon_external_created', true, true);" " window._falkon_external = true;" " document.dispatchEvent(event);" " }" "}" "" "if (self !== top) {" " if (top._falkon_external)" " registerExternal(top.external);" " else" " top.document.addEventListener('_falkon_external_created', function() {" " registerExternal(top.external);" " });" " return;" "}" "" "function registerWebChannel() {" " try {" " new QWebChannel(qt.webChannelTransport, function(channel) {" " var external = channel.objects.qz_object;" " external.extra = {};" " for (var key in channel.objects) {" " if (key != 'qz_object' && key.startsWith('qz_')) {" " external.extra[key.substr(3)] = channel.objects[key];" " }" " }" " registerExternal(external);" " });" " } catch (e) {" " setTimeout(registerWebChannel, 100);" " }" "}" "registerWebChannel();" "" "})()"); QString match; if (worldId == WebPage::SafeJsWorld) { match = QSL("// @exclude falkon:*\n// @exclude extension:*"); } else { match = QSL("// @include falkon:*\n// @include extension:*"); } return source.arg(match, QzTools::readAllFileContents(QSL(":/qtwebchannel/qwebchannel.js"))); } QString Scripts::setupFormObserver() { QString source = QL1S("(function() {" "function findUsername(inputs) {" " var usernameNames = ['user', 'name', 'login'];" " for (var i = 0; i < usernameNames.length; ++i) {" " for (var j = 0; j < inputs.length; ++j)" " if (inputs[j].type == 'text' && inputs[j].value.length && inputs[j].name.indexOf(usernameNames[i]) != -1)" " return inputs[j].value;" " }" " for (var i = 0; i < inputs.length; ++i)" " if (inputs[i].type == 'text' && inputs[i].value.length)" " return inputs[i].value;" " for (var i = 0; i < inputs.length; ++i)" " if (inputs[i].type == 'email' && inputs[i].value.length)" " return inputs[i].value;" " return '';" "}" "" "function registerForm(form) {" " form.addEventListener('submit', function() {" " var form = this;" " var data = '';" " var password = '';" " var inputs = form.getElementsByTagName('input');" " for (var i = 0; i < inputs.length; ++i) {" " var input = inputs[i];" " var type = input.type.toLowerCase();" " if (type != 'text' && type != 'password' && type != 'email')" " continue;" " if (!password && type == 'password')" " password = input.value;" " data += encodeURIComponent(input.name);" " data += '=';" " data += encodeURIComponent(input.value);" " data += '&';" " }" " if (!password)" " return;" " data = data.substring(0, data.length - 1);" " var url = window.location.href;" " var username = findUsername(inputs);" " external.autoFill.formSubmitted(url, username, password, data);" " }, true);" "}" "" "if (!document.documentElement) return;" "" "for (var i = 0; i < document.forms.length; ++i)" " registerForm(document.forms[i]);" "" "var observer = new MutationObserver(function(mutations) {" " for (var i = 0; i < mutations.length; ++i)" " for (var j = 0; j < mutations[i].addedNodes.length; ++j)" " if (mutations[i].addedNodes[j].tagName == 'FORM')" " registerForm(mutations[i].addedNodes[j]);" "});" "observer.observe(document.documentElement, { childList: true, subtree: true });" "" "})()"); return source; } QString Scripts::setupWindowObject() { QString source = QL1S("(function() {" "var external = {};" "external.AddSearchProvider = function(url) {" " window.location = 'falkon:AddSearchProvider?url=' + url;" "};" "external.IsSearchProviderInstalled = function(url) {" " console.warn('NOT IMPLEMENTED: IsSearchProviderInstalled()');" " return false;" "};" "window.external = external;" "window.print = function() {" " window.location = 'falkon:PrintPage';" "};" "})()"); return source; } QString Scripts::setCss(const QString &css) { QString source = QL1S("(function() {" "var head = document.getElementsByTagName('head')[0];" "if (!head) return;" "var css = document.createElement('style');" "css.setAttribute('type', 'text/css');" "css.appendChild(document.createTextNode('%1'));" "head.appendChild(css);" "})()"); QString style = css; style.replace(QL1S("'"), QL1S("\\'")); style.replace(QL1S("\n"), QL1S("\\n")); return source.arg(style); } QString Scripts::sendPostData(const QUrl &url, const QByteArray &data) { QString source = QL1S("(function() {" "var form = document.createElement('form');" "form.setAttribute('method', 'POST');" "form.setAttribute('action', '%1');" "var val;" "%2" "form.submit();" "})()"); QString valueSource = QL1S("val = document.createElement('input');" "val.setAttribute('type', 'hidden');" "val.setAttribute('name', '%1');" "val.setAttribute('value', '%2');" "form.appendChild(val);"); QString values; QUrlQuery query(data); const auto &queryItems = query.queryItems(QUrl::FullyDecoded); for (int i = 0; i < queryItems.size(); ++i) { const auto &pair = queryItems[i]; QString value = pair.first; QString key = pair.second; value.replace(QL1S("'"), QL1S("\\'")); key.replace(QL1S("'"), QL1S("\\'")); values.append(valueSource.arg(value, key)); } return source.arg(url.toString(), values); } QString Scripts::completeFormData(const QByteArray &data) { QString source = QL1S("(function() {" "var data = '%1'.split('&');" "var inputs = document.getElementsByTagName('input');" "" "for (var i = 0; i < data.length; ++i) {" " var pair = data[i].split('=');" " if (pair.length != 2)" " continue;" " var key = decodeURIComponent(pair[0]);" " var val = decodeURIComponent(pair[1]);" " for (var j = 0; j < inputs.length; ++j) {" " var input = inputs[j];" " var type = input.type.toLowerCase();" " if (type != 'text' && type != 'password' && type != 'email')" " continue;" " if (input.name == key)" " input.value = val;" " }" "}" "" "})()"); QString d = data; d.replace(QL1S("'"), QL1S("\\'")); return source.arg(d); } QString Scripts::getOpenSearchLinks() { QString source = QL1S("(function() {" "var out = [];" "var links = document.getElementsByTagName('link');" "for (var i = 0; i < links.length; ++i) {" " var e = links[i];" " if (e.type == 'application/opensearchdescription+xml') {" " out.push({" " url: e.href," " title: e.title" " });" " }" "}" "return out;" "})()"); return source; } QString Scripts::getAllImages() { QString source = QL1S("(function() {" "var out = [];" "var imgs = document.getElementsByTagName('img');" "for (var i = 0; i < imgs.length; ++i) {" " var e = imgs[i];" " out.push({" " src: e.src," " alt: e.alt" " });" "}" "return out;" "})()"); return source; } QString Scripts::getAllMetaAttributes() { QString source = QL1S("(function() {" "var out = [];" "var meta = document.getElementsByTagName('meta');" "for (var i = 0; i < meta.length; ++i) {" " var e = meta[i];" " out.push({" " name: e.getAttribute('name')," " content: e.getAttribute('content')," " httpequiv: e.getAttribute('http-equiv')" " });" "}" "return out;" "})()"); return source; } QString Scripts::getFormData(const QPointF &pos) { QString source = QL1S("(function() {" "var e = document.elementFromPoint(%1, %2);" "if (!e || e.tagName != 'INPUT')" " return;" "var fe = e.parentElement;" "while (fe) {" " if (fe.tagName == 'FORM')" " break;" " fe = fe.parentElement;" "}" "if (!fe)" " return;" "var res = {" " method: fe.method.toLowerCase()," " action: fe.action," " inputName: e.name," " inputs: []," "};" "for (var i = 0; i < fe.length; ++i) {" " var input = fe.elements[i];" " res.inputs.push([input.name, input.value]);" "}" "return res;" "})()"); return source.arg(pos.x()).arg(pos.y()); }