pax_global_header00006660000000000000000000000064140752763230014523gustar00rootroot0000000000000052 comment=6c07c3d7c2f98327b269c895fe5362a36b1b164e angelfish-v21.07/000077500000000000000000000000001407527632300136625ustar00rootroot00000000000000angelfish-v21.07/.gitignore000066400000000000000000000000161407527632300156470ustar00rootroot00000000000000.clang-format angelfish-v21.07/.gitlab-ci.yml000066400000000000000000000004641407527632300163220ustar00rootroot00000000000000include: - https://invent.kde.org/sysadmin/ci-tooling/raw/master/invent/binary-flatpak.yml variables: BUNDLE: org.kde.mobile.angelfish.flatpak flatpak: extends: .flatpak variables: MANIFEST_PATH: org.kde.mobile.angelfish.json APP_ID: org.kde.mobile.angelfish FLATPAK_MODULE: angelfish angelfish-v21.07/.gitlab/000077500000000000000000000000001407527632300152025ustar00rootroot00000000000000angelfish-v21.07/.gitlab/issue_templates/000077500000000000000000000000001407527632300204105ustar00rootroot00000000000000angelfish-v21.07/.gitlab/issue_templates/default_issue.md000066400000000000000000000017071407527632300235730ustar00rootroot00000000000000Thank you for contributing to the Angelfish development. Please have a look at the following issue template, write your text below it and remove the template including this message when you are finished. Thanks. ## What kind of issue do you want to open? * [ ] **Bug**: Please mention the distribution and user interface you are running Angelfish on. * [ ] **Feature request**: * [ ] **I want to work on the feature**: Feel free to use this issue as tracking issue for your work * [ ] **I don't want to work on the feature**: If you think you have a cool new idea, please describe it. But please try not to open issues for obvious missing features, to keep the number of issues down to make it easier to find tracking issues and bug reports. If the issue tracker gets to crowded, only the latest issues get attention and important tasks might be overlooked, which would defeat the purpose of the issue tracker. angelfish-v21.07/CMakeLists.txt000066400000000000000000000055251407527632300164310ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler # SPDX-FileCopyrightText: 2020 Jonah Brüchert # # SPDX-License-Identifier: LGPL-2.0-or-later project(angelfish) cmake_minimum_required(VERSION 2.8.12) set(KF5_MIN_VERSION "5.84.0") set(QT_MIN_VERSION "5.15.0") set(PROJECT_VERSION "21.07") set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) option(BUILD_TESTING "Build test programs" ON) ################# Disallow in-source build ################# if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") message(FATAL_ERROR "This application requires an out of source build. Please create a separate build directory.") endif() include(FeatureSummary) ################# set KDE specific information ################# find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(ECMSetupVersion) include(ECMGenerateHeaders) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEClangFormat) include(ECMPoQmTools) include(KDECompilerSettings NO_POLICY_SCOPE) ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX ANGELFISH VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/version.h ) ################# Find dependencies ################# find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Core Quick Test Gui Svg QuickControls2 Sql Feedback) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Purpose I18n Config CoreAddons DBusAddons WindowSystem Notifications) # Necessary to support QtWebEngine installed in a different prefix than the rest of Qt (e.g flatpak) find_package(Qt5WebEngine REQUIRED) # For adblocker find_package(Corrosion) set_package_properties(Corrosion PROPERTIES PURPOSE "Required to build the builtin adblocker" DESCRIPTION "CMake scripts to seamlessly build and link to targets using cargo" URL https://github.com/AndrewGaspar/corrosion ) ################# Definitions to pass to the compiler ################# add_definitions(-DQT_NO_FOREACH) kde_enable_exceptions() ################# build and install ################# add_subdirectory(lib) add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) endif() add_subdirectory(angelfish-webapp) install(PROGRAMS org.kde.angelfish.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES org.kde.angelfish.metainfo.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install(FILES org.kde.angelfish.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h lib/*.cpp lib/*.h angelfish-webapp/*.cpp angelfish-webapp/*.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) angelfish-v21.07/Cargo.lock000066400000000000000000000322101407527632300155650ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "adblock" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e9106f026aebe0d8924a0c520e999b406629cd5b336c0b19743542ec1092134" dependencies = [ "addr", "base64", "bitflags", "flate2", "idna", "itertools", "lifeguard", "once_cell", "percent-encoding", "regex", "rmp-serde 0.13.7", "rmp-serde 0.15.5", "seahash", "serde", "twoway", "url", ] [[package]] name = "addr" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c54ccac949a2afafdfc889e15c753bbc6ee8783e026bbe3d057b00b13907db70" dependencies = [ "psl", "psl-types", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] name = "angelfish-adblock" version = "0.1.0" dependencies = [ "adblock", "cxx", "cxx-build", ] [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "codespan-reporting" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", "unicode-width", ] [[package]] name = "crc32fast" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ "cfg-if", ] [[package]] name = "cxx" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65369ae12626651c9d8c5ca8b44ffdb9fe7ba710508074d2997315d2fe4f4276" dependencies = [ "cc", "cxxbridge-flags", "cxxbridge-macro", "link-cplusplus", ] [[package]] name = "cxx-build" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f5bde7453f995f4edcbe37c653dbc434018a4fda5941b3437005a36f69b156a" dependencies = [ "cc", "codespan-reporting", "lazy_static", "proc-macro2", "quote", "scratch", "syn", ] [[package]] name = "cxxbridge-flags" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9198d12d90c4a743fa06d45901ea419b81f250ad8f97635763cfaa791429ba1" [[package]] name = "cxxbridge-macro" version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63138f3cc0d24ef4c5a71e0def78e5375bd52d17a58cefc645b091c361dc7c3e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "flate2" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ "cfg-if", "crc32fast", "libc", "miniz_oxide", ] [[package]] name = "form_urlencoded" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ "matches", "percent-encoding", ] [[package]] name = "idna" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "itertools" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "lifeguard" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89be94dbd775db37b46ca4f4bf5cf89adfb13ba197bfbcb69b2122848ee73c26" [[package]] name = "link-cplusplus" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1" dependencies = [ "cc", ] [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "miniz_oxide" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", "autocfg", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "proc-macro2" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] [[package]] name = "psl" version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3da8b9253dcf3c831625a3d49a203f8972066478f4b808cbbb5eb17ef1aec75" dependencies = [ "psl-types", ] [[package]] name = "psl-types" version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b398073e7cdd6f05934389a8f5961e3aabfa66675b6f440df4e2c793d51a4f" [[package]] name = "quote" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rmp" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6" dependencies = [ "byteorder", "num-traits", ] [[package]] name = "rmp-serde" version = "0.13.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3" dependencies = [ "byteorder", "rmp", "serde", ] [[package]] name = "rmp-serde" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d" dependencies = [ "byteorder", "rmp", "serde", ] [[package]] name = "scratch" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" [[package]] name = "seahash" version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f57ca1d128a43733fd71d583e837b1f22239a37ebea09cde11d8d9a9080f47" [[package]] name = "serde" version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[package]] name = "tinyvec" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "twoway" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47" dependencies = [ "memchr", "unchecked-index", ] [[package]] name = "unchecked-index" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" [[package]] name = "unicode-bidi" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" dependencies = [ "matches", ] [[package]] name = "unicode-normalization" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "url" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", "idna", "matches", "percent-encoding", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" angelfish-v21.07/Cargo.toml000066400000000000000000000001331407527632300156070ustar00rootroot00000000000000[workspace] members = [ "src/rs/adblock/" ] [profile.release] lto = true panic = 'abort' angelfish-v21.07/LICENSES/000077500000000000000000000000001407527632300150675ustar00rootroot00000000000000angelfish-v21.07/LICENSES/GPL-2.0-or-later.txt000066400000000000000000000415751407527632300203060ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice angelfish-v21.07/LICENSES/LGPL-2.0-only.txt000066400000000000000000000603161407527632300176500ustar00rootroot00000000000000GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! angelfish-v21.07/LICENSES/LGPL-2.0-or-later.txt000066400000000000000000000603161407527632300204140ustar00rootroot00000000000000GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! angelfish-v21.07/LICENSES/MIT.txt000066400000000000000000000020661407527632300162650ustar00rootroot00000000000000MIT License Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. angelfish-v21.07/Messages.sh000066400000000000000000000001271407527632300157650ustar00rootroot00000000000000$XGETTEXT $(find . -name \*.cpp -o -name \*.h -o -name \*.qml) -o $podir/angelfish.pot angelfish-v21.07/README.md000066400000000000000000000033321407527632300151420ustar00rootroot00000000000000# Angelfish This is a webbrowser designed to - be used on small mobile devices, - integrate well in Plasma workspaces Download on Flathub ## Reporting bugs Bugtracker: https://invent.kde.org/plasma-mobile/angelfish/-/issues Please choose the "default_issue" description template when opening issues. ## Preliminary roadmap: - [x] browser navigation: back + forward + reload - [x] browser status - [x] Implement URL bar - [x] Error handler in UI - [x] history store, model and UI - [x] bookmarks store, model and UI - [x] add / remove - [x] in-window navigation: tabs in bottom bar - [ ] SSL error handler - [x] Touch actions (pinch?) (done in QtWebEngine) - [x] user-agent to request mobile site - [x] open and close new tabs - [x] History based completion - [x] Right click / long press menu - [x] purpose integration (for kdeconnect) - [x] adblock ## Development To debug requests sent by the browser, for example for debugging the ad blocker, it can be useful to have a look at the development tools. For using them, the browser needs to be started with a special environment variable set: `QTWEBENGINE_REMOTE_DEBUGGING=4321 angelfish`. The variable contains the port on which the development tools will be available. You can now point another browser to http://localhost:4321. To enable adblock logging, add the following to `~/.config/QtProject/qtlogging.ini`: ``` [Rules] org.kde.angelfish.adblock.debug=true ``` ### Flatpak If one of the Cargo.toml files is updated, the flatpak sources need to be regenerated. That can be done using the `./flatpak/regenerate-sources.sh` script. angelfish-v21.07/angelfish-webapp/000077500000000000000000000000001407527632300170765ustar00rootroot00000000000000angelfish-v21.07/angelfish-webapp/CMakeLists.txt000066400000000000000000000013231407527632300216350ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2020 Jonah Brüchert # SPDX-FileCopyrightText: 2020 Rinigus # # SPDX-License-Identifier: LGPL-2.0-or-later set(angelfish_webapp_SRCS main.cpp webapp-resources.qrc ) add_executable(angelfish-webapp ${angelfish_webapp_SRCS} ${RESOURCES} ${WEBAPP_RESOURCES}) target_include_directories(angelfish-webapp PRIVATE ../src/) target_compile_definitions(angelfish-webapp PRIVATE -DQT_NO_CAST_FROM_ASCII) target_link_libraries(angelfish-webapp Qt5::Core Qt5::Qml Qt5::Quick Qt5::Sql Qt5::Svg Qt5::WebEngine KF5::I18n KF5::CoreAddons AngelfishCore ) install(TARGETS angelfish-webapp ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) angelfish-v21.07/angelfish-webapp/contents/000077500000000000000000000000001407527632300207335ustar00rootroot00000000000000angelfish-v21.07/angelfish-webapp/contents/ui/000077500000000000000000000000001407527632300213505ustar00rootroot00000000000000angelfish-v21.07/angelfish-webapp/contents/ui/WebAppView.qml000066400000000000000000000047431407527632300241040ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Window 2.1 import QtQuick.Layouts 1.3 import QtWebEngine 1.7 import org.kde.kirigami 2.4 as Kirigami import org.kde.angelfish 1.0 WebView { id: webEngineView profile: AngelfishWebProfile { httpUserAgent: userAgent.userAgent questionLoader: questionLoader offTheRecord: false storageName: "angelfish-webapp" } // Custom context menu contextMenu: Controls.Menu { property ContextMenuRequest request id: contextMenu Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanCopy) != 0 text: i18n("Copy") onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy) } Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanCut) != 0 text: i18n("Cut") onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut) } Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanPaste) != 0 text: i18n("Paste") onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste) } Controls.MenuItem { enabled: contextMenu.request && contextMenu.request.selectedText text: contextMenu.request && contextMenu.request.selectedText ? i18n("Search online for '%1'", contextMenu.request.selectedText) : i18n("Search online") onTriggered: Qt.openUrlExternally(UrlUtils.urlFromUserInput(Settings.searchBaseUrl + contextMenu.request.selectedText)); } Controls.MenuItem { enabled: contextMenu.request && contextMenu.request.linkUrl !== "" text: i18n("Copy Url") onTriggered: webEngineView.triggerWebAction(WebEngineView.CopyLinkToClipboard) } Controls.MenuItem { text: i18n("Download") onTriggered: webEngineView.triggerWebAction(WebEngineView.DownloadLinkToDisk) } } onNewViewRequested: { if (UrlUtils.urlHost(request.requestedUrl) === UrlUtils.urlHost( BrowserManager.initialUrl)) { url = request.requestedUrl; } else { Qt.openUrlExternally(request.requestedUrl); } } } angelfish-v21.07/angelfish-webapp/contents/ui/webapp.qml000066400000000000000000000050741407527632300233470ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.1 import QtWebEngine 1.6 import QtQuick.Window 2.3 import QtGraphicalEffects 1.0 import Qt.labs.settings 1.0 as QtSettings import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 import QtQuick.Layouts 1.2 Kirigami.ApplicationWindow { id: webBrowser title: currentWebView.title minimumWidth: Kirigami.Units.gridUnit * 20 minimumHeight: Kirigami.Units.gridUnit * 30 pageStack.globalToolBar.showNavigationButtons: false // Main Page pageStack.initialPage: Kirigami.Page { id: rootPage leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None Kirigami.ColumnView.fillWidth: true Kirigami.ColumnView.pinned: true Kirigami.ColumnView.preventStealing: true WebAppView { // ID for compatibility with angelfish components id: currentWebView anchors.fill: parent url: BrowserManager.initialUrl } ErrorHandler { id: errorHandler errorString: currentWebView.errorString errorCode: currentWebView.errorCode anchors.fill: parent visible: currentWebView.errorCode !== "" onRefreshRequested: currentWebView.reload() } Loader { id: questionLoader anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right } // Container for the progress bar Item { id: progressItem height: Math.round(Kirigami.Units.gridUnit / 6) z: 99 anchors { bottom: parent.bottom bottomMargin: -Math.round(height / 2) left: webBrowser.left right: webBrowser.right } opacity: currentWebView.loading ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } Rectangle { color: Kirigami.Theme.highlightColor width: Math.round((currentWebView.loadProgress / 100) * parent.width) anchors { top: parent.top left: parent.left bottom: parent.bottom } } } Loader { id: sheetLoader } } } angelfish-v21.07/angelfish-webapp/main.cpp000066400000000000000000000106311407527632300205270ustar00rootroot00000000000000/* SPDX-FileCopyrightText: 2019 Jonah Brüchert SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include "angelfishsettings.h" #include "angelfishwebprofile.h" #include "bookmarkshistorymodel.h" #include "browsermanager.h" #include "iconimageprovider.h" #include "tabsmodel.h" #include "urlutils.h" #include "useragent.h" constexpr auto APPLICATION_ID = "org.kde.angelfish"; QString desktopFileDirectory() { if (!QStandardPaths::locate(QStandardPaths::RuntimeLocation, QStringLiteral("flatpak-info")).isEmpty()) { return qEnvironmentVariable("HOME") % u"/.local/share/applications/"; } return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); } Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts); KLocalizedString::setApplicationDomain("angelfish"); // Setup QtWebEngine qputenv("QTWEBENGINE_DIALOG_SET", "QtQuickControls2"); #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif QApplication app(argc, argv); // QCoreApplication::setOrganizationName("KDE"); // QCoreApplication::setOrganizationDomain("mobile.kde.org"); // QCoreApplication::setApplicationName("angelfish"); #if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif // Command line parser QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("desktopfile"), i18n("desktop file to open"), QStringLiteral("[file]")); parser.addHelpOption(); parser.process(app); // QML loading QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider(&engine)); if (parser.positionalArguments().isEmpty()) { return 1; } const QString fileName = parser.positionalArguments().constFirst(); const QString path = desktopFileDirectory() % QDir::separator() % fileName; const KDesktopFile desktopFile(path); if (desktopFile.readUrl().isEmpty()) { qDebug() << "Failed to find url in" << path; return 2; } const QString initialUrl = QUrl::fromUserInput(desktopFile.readUrl()).toString(); const QString appName = desktopFile.readName().toLower().replace(QLatin1Char(' '), QLatin1Char('-')) + QStringLiteral("-angelfish-webapp"); KAboutData aboutData(appName.toLower(), desktopFile.readName(), QStringLiteral("0.1"), i18n("Angelfish Web App runtime"), KAboutLicense::GPL, i18n("Copyright 2020 Angelfish developers")); QApplication::setWindowIcon(QIcon::fromTheme(desktopFile.readIcon())); aboutData.addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); KAboutData::setApplicationData(aboutData); // Exported types qmlRegisterType(APPLICATION_ID, 1, 0, "BookmarksHistoryModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "UserAgentGenerator"); qmlRegisterType(APPLICATION_ID, 1, 0, "TabsModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "AngelfishWebProfile"); // URL utils qmlRegisterSingletonType(APPLICATION_ID, 1, 0, "UrlUtils", [](QQmlEngine *, QJSEngine *) -> QObject * { return new UrlUtils(); }); BrowserManager::instance()->setInitialUrl(initialUrl); // Browser Manager qmlRegisterSingletonType(APPLICATION_ID, 1, 0, "BrowserManager", [](QQmlEngine *, QJSEngine *) -> QObject * { return BrowserManager::instance(); }); // Settings are read from WebView which we use as super class for WebAppView qmlRegisterSingletonInstance(APPLICATION_ID, 1, 0, "Settings", AngelfishSettings::self()); Q_INIT_RESOURCE(resources); // Load QML engine.load(QUrl(QStringLiteral("qrc:///webapp.qml"))); // Error handling if (engine.rootObjects().isEmpty()) { return -1; } return app.exec(); } angelfish-v21.07/angelfish-webapp/webapp-resources.qrc000066400000000000000000000002771407527632300231010ustar00rootroot00000000000000 contents/ui/webapp.qml contents/ui/WebAppView.qml angelfish-v21.07/autotests/000077500000000000000000000000001407527632300157155ustar00rootroot00000000000000angelfish-v21.07/autotests/CMakeLists.txt000066400000000000000000000015161407527632300204600ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2020 Jonah Brüchert # SPDX-FileCopyrightText: 2020 Rinigus # # SPDX-License-Identifier: LGPL-2.0-or-later include(ECMAddTests) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test Sql Gui Quick) ecm_add_test(dbmanagertest.cpp TEST_NAME dbmanagertest LINK_LIBRARIES Qt5::Test Qt5::Sql Qt5::Quick AngelfishCore ) ecm_add_test(browsermanagertest.cpp TEST_NAME browsermanagertest LINK_LIBRARIES Qt5::Test Qt5::Sql Qt5::Gui Qt5::Quick AngelfishCore ) ecm_add_test(tabsmodeltest.cpp TEST_NAME tabsmodeltest LINK_LIBRARIES Qt5::Test Qt5::Sql Qt5::Gui Qt5::Quick AngelfishCore ) ecm_add_test(configtest.cpp TEST_NAME configtest LINK_LIBRARIES Qt5::Test AngelfishCore ) angelfish-v21.07/autotests/browsermanagertest.cpp000066400000000000000000000021731407527632300223420ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2019 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include #include #include #include #include "browsermanager.h" #include "urlutils.h" #include "angelfishsettings.h" class UserAgentTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { QCoreApplication::setOrganizationName("autotests"); QCoreApplication::setApplicationName("angelfish_dbmanagertest"); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); dir.mkpath("."); m_browserManager = BrowserManager::instance(); } void cleanupTestCase() { delete m_browserManager; } void urlFromUserInput() { const QString incompleteUrl = QStringLiteral("kde.org"); const QString completeUrl = QStringLiteral("http://kde.org"); QCOMPARE(UrlUtils::urlFromUserInput(incompleteUrl), completeUrl); } private: BrowserManager *m_browserManager; }; QTEST_GUILESS_MAIN(UserAgentTest); #include "browsermanagertest.moc" angelfish-v21.07/autotests/configtest.cpp000066400000000000000000000022431407527632300205670ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include #include "angelfishsettings.h" class ConfigTest : public QObject { Q_OBJECT private Q_SLOTS: void testDefaultValues() { QCOMPARE(AngelfishSettings::defaultHomepageValue(), "https://start.duckduckgo.com"); QCOMPARE(AngelfishSettings::defaultSearchBaseUrlValue(), "https://start.duckduckgo.com/?q="); QCOMPARE(AngelfishSettings::defaultWebAutoLoadImagesValue(), true); QCOMPARE(AngelfishSettings::defaultWebJavaScriptEnabledValue(), true); QCOMPARE(AngelfishSettings::defaultNavBarMainMenuValue(), true); QCOMPARE(AngelfishSettings::defaultNavBarTabsValue(), true); } void testSettingsHelper() { qputenv("QT_QUICK_CONTROLS_MOBILE", "true"); QCOMPARE(SettingsHelper::isMobile(), true); QCOMPARE(AngelfishSettings::defaultNavBarBackValue(), false); QCOMPARE(AngelfishSettings::defaultNavBarForwardValue(), false); QCOMPARE(AngelfishSettings::defaultNavBarReloadValue(), false); } }; QTEST_GUILESS_MAIN(ConfigTest); #include "configtest.moc" angelfish-v21.07/autotests/data/000077500000000000000000000000001407527632300166265ustar00rootroot00000000000000angelfish-v21.07/autotests/data/simplebookmarks.json000066400000000000000000000023171407527632300227260ustar00rootroot00000000000000[ { "bookmarked": true, "icon": "text-html", "lastVisited": "Sat Dec 13 02:29:58 2014", "title": "Nieuws", "url": "http://m.nos.nl" }, { "bookmarked": true, "icon": "/home/sebas/Pictures/avatar-small.jpg", "lastVisited": "Sat Dec 13 02:29:58 2014", "preview": "/home/sebas/Pictures/avatar-small.jpg", "title": "sebas' blog", "url": "http://vizZzion.org" }, { "bookmarked": true, "icon": "text-html", "lastVisited": "Sat Dec 13 02:29:58 2014", "title": "Linux Weekly News", "url": "http://lwn.net" }, { "bookmarked": true, "icon": "text-html", "lastVisited": "Sat Dec 13 02:29:58 2014", "title": "Tweakers.net", "url": "http://tweakers.net" }, { "bookmarked": false, "icon": "text-html", "lastVisited": "Sat Dec 13 02:29:58 2014", "title": "Wikipedia", "url": "http://en.wikipedia.org" }, { "bookmarked": true, "icon": "plasma", "lastVisited": "Sat Dec 13 02:29:58 2014", "title": "Plasma Mobile", "url": "http://plasma-mobile.org" } ] angelfish-v21.07/autotests/dbmanagertest.cpp000066400000000000000000000045401407527632300212440ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include #include #include #include #include #include "dbmanager.h" #include "sqlquerymodel.h" class TabsModelTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { QCoreApplication::setOrganizationName("autotests"); QCoreApplication::setApplicationName("angelfish_dbmanagertest"); QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); dir.mkpath("."); m_dbmanager = new DBManager(); } void testAddBookmark() { QSignalSpy spy(m_dbmanager, &DBManager::databaseTableChanged); m_dbmanager->addBookmark({{"url", "https://kde.org"}, {"title", "KDE"}, {"icon", "TESTDATA"}}); QCOMPARE(spy.count(), 1); } void testAddToHistory() { QSignalSpy spy(m_dbmanager, &DBManager::databaseTableChanged); m_dbmanager->addToHistory({{"url", "https://kde.org"}, {"title", "KDE"}, {"icon", "TESTDATA"}}); QCOMPARE(spy.count(), 1); } void testLastVisited() { QSignalSpy spy(m_dbmanager, &DBManager::databaseTableChanged); m_dbmanager->updateLastVisited("https://kde.org"); // Will be updated in both tables QCOMPARE(spy.count(), 2); } void testRemoveBookmark() { QSignalSpy spy(m_dbmanager, &DBManager::databaseTableChanged); m_dbmanager->removeBookmark("https://kde.org"); QCOMPARE(spy.count(), 1); } void testRemoveFromHistory() { QSignalSpy spy(m_dbmanager, &DBManager::databaseTableChanged); m_dbmanager->removeBookmark("https://kde.org"); QCOMPARE(spy.count(), 1); } void testSqlQueryModelRoleNames() { auto model = new SqlQueryModel(); model->setQuery(QSqlQuery("SELECT * FROM history")); QHash expectedRoleNames = { { Qt::UserRole + 1, "url"}, { Qt::UserRole + 2, "title"}, { Qt::UserRole + 3, "icon"}, { Qt::UserRole + 4, "lastVisited"} }; QCOMPARE(model->roleNames(), expectedRoleNames); } private: DBManager *m_dbmanager; }; QTEST_GUILESS_MAIN(TabsModelTest); #include "dbmanagertest.moc" angelfish-v21.07/autotests/tabsmodeltest.cpp000066400000000000000000000073321407527632300213000ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include #include "tabsmodel.h" class TabsModelTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { m_tabsModel = new TabsModel(); } void testInitialTabExists() { QCOMPARE(m_tabsModel->tabs().count(), 1); // Current tab should be initial tab QCOMPARE(m_tabsModel->currentTab(), 0); QCOMPARE(m_tabsModel->tab(0).url(), "about:blank"); } void testNewTab() { m_tabsModel->newTab("https://kde.org"); QCOMPARE(m_tabsModel->tabs().count(), 2); qDebug() << m_tabsModel->tab(1).url() << m_tabsModel->tab(0).isMobile(); QCOMPARE(m_tabsModel->tab(1).url(), "https://kde.org"); // newly created tab should be current tab now QCOMPARE(m_tabsModel->currentTab(), 1); } void testCurrentTab() { QCOMPARE(m_tabsModel->tabs().at(m_tabsModel->currentTab()).url(), "https://kde.org"); } void testCloseTab() { // Close initial tab, keep kde.org one m_tabsModel->closeTab(0); QCOMPARE(m_tabsModel->tabs().count(), 1); // Check tabs moved properly QCOMPARE(m_tabsModel->tabs().at(0).url(), "https://kde.org"); } void testLoad() { m_tabsModel->setUrl(0, "https://debian.org"); // Number of tabs must not change QCOMPARE(m_tabsModel->tabs().count(), 1); QCOMPARE(m_tabsModel->tabs().at(0).url(), "https://debian.org"); } void testRowCountMatches() { QCOMPARE(m_tabsModel->tabs().count(), m_tabsModel->rowCount()); } void testCloseCurrentTab() { // // Case 1: There is only one tab, a new one should be created // QCOMPARE(m_tabsModel->tabs().count(), 1); m_tabsModel->setCurrentTab(0); m_tabsModel->closeTab(0); // Check whether a new empty tab was created (count must not be less than one) QCOMPARE(m_tabsModel->tabs().count(), 1); QCOMPARE(m_tabsModel->tabs().at(0).url(), "about:blank"); // // Case 2: There are multiple tabs // m_tabsModel->newTab("second"); m_tabsModel->newTab("third"); QCOMPARE(m_tabsModel->tabs(), QVector({ TabState("about:blank", m_tabsModel->isMobileDefault()), TabState("second", m_tabsModel->isMobileDefault()), TabState("third", m_tabsModel->isMobileDefault()) })); // current tab is 2 // close tab "second" m_tabsModel->closeTab(1); // current tab should now be 0, since we reset to first tab if the current tab is closed QCOMPARE(m_tabsModel->currentTab(), 0); // "second" is indeed gone QCOMPARE(m_tabsModel->tabs(), QVector({ TabState("about:blank", m_tabsModel->isMobileDefault()), TabState("third", m_tabsModel->isMobileDefault()) })); } void testSetTab() { m_tabsModel->setUrl(0, QStringLiteral("https://debian.org")); QCOMPARE(m_tabsModel->tabs(), QVector({ TabState("https://debian.org", m_tabsModel->isMobileDefault()), TabState("third", m_tabsModel->isMobileDefault())} )); } void testPrivateMode() { // private mode is off by default QCOMPARE(m_tabsModel->privateMode(), false); m_tabsModel->setPrivateMode(true); QCOMPARE(m_tabsModel->privateMode(), true); m_tabsModel->setPrivateMode(false); QCOMPARE(m_tabsModel->privateMode(), false); } private: TabsModel *m_tabsModel; }; QTEST_GUILESS_MAIN(TabsModelTest); #include "tabsmodeltest.moc" angelfish-v21.07/flatpak/000077500000000000000000000000001407527632300153045ustar00rootroot00000000000000angelfish-v21.07/flatpak/corrosion-generated-sources.json000066400000000000000000000300041407527632300236260ustar00rootroot00000000000000[ { "type": "file", "url": "https://static.crates.io/crates/bitflags/bitflags-1.2.1.crate", "sha256": "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693", "dest": "cargo/vendor", "dest-filename": "bitflags-1.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/bitflags-1.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/camino/camino-1.0.4.crate", "sha256": "d4648c6d00a709aa069a236adcaae4f605a6241c72bf5bee79331a4b625921a9", "dest": "cargo/vendor", "dest-filename": "camino-1.0.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d4648c6d00a709aa069a236adcaae4f605a6241c72bf5bee79331a4b625921a9%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/camino-1.0.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cargo-platform/cargo-platform-0.1.1.crate", "sha256": "0226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7", "dest": "cargo/vendor", "dest-filename": "cargo-platform-0.1.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%220226944a63d1bf35a3b5f948dd7c59e263db83695c9e8bffc4037de02e30f1d7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cargo-platform-0.1.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cargo_metadata/cargo_metadata-0.13.1.crate", "sha256": "081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8", "dest": "cargo/vendor", "dest-filename": "cargo_metadata-0.13.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22081e3f0755c1f380c2d010481b6fa2e02973586d5f2b24eebb7a2a1d98b143d8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cargo_metadata-0.13.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/clap/clap-2.33.3.crate", "sha256": "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002", "dest": "cargo/vendor", "dest-filename": "clap-2.33.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2237e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/clap-2.33.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/itoa/itoa-0.4.7.crate", "sha256": "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736", "dest": "cargo/vendor", "dest-filename": "itoa-0.4.7.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/itoa-0.4.7", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/pest/pest-2.1.3.crate", "sha256": "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53", "dest": "cargo/vendor", "dest-filename": "pest-2.1.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2210f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/pest-2.1.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/platforms/platforms-1.1.0.crate", "sha256": "989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325", "dest": "cargo/vendor", "dest-filename": "platforms-1.1.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22989d43012e2ca1c4a02507c67282691a0a3207f9dc67cec596b43fe925b3d325%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/platforms-1.1.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/proc-macro2/proc-macro2-1.0.26.crate", "sha256": "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec", "dest": "cargo/vendor", "dest-filename": "proc-macro2-1.0.26.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/proc-macro2-1.0.26", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/quote/quote-1.0.9.crate", "sha256": "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7", "dest": "cargo/vendor", "dest-filename": "quote-1.0.9.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/quote-1.0.9", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/ryu/ryu-1.0.5.crate", "sha256": "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e", "dest": "cargo/vendor", "dest-filename": "ryu-1.0.5.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2271d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/ryu-1.0.5", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/semver/semver-0.11.0.crate", "sha256": "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6", "dest": "cargo/vendor", "dest-filename": "semver-0.11.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/semver-0.11.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/semver-parser/semver-parser-0.10.2.crate", "sha256": "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7", "dest": "cargo/vendor", "dest-filename": "semver-parser-0.10.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2200b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/semver-parser-0.10.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde/serde-1.0.125.crate", "sha256": "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171", "dest": "cargo/vendor", "dest-filename": "serde-1.0.125.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde-1.0.125", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde_derive/serde_derive-1.0.125.crate", "sha256": "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d", "dest": "cargo/vendor", "dest-filename": "serde_derive-1.0.125.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde_derive-1.0.125", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde_json/serde_json-1.0.64.crate", "sha256": "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79", "dest": "cargo/vendor", "dest-filename": "serde_json-1.0.64.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde_json-1.0.64", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/syn/syn-1.0.71.crate", "sha256": "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373", "dest": "cargo/vendor", "dest-filename": "syn-1.0.71.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/syn-1.0.71", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/textwrap/textwrap-0.11.0.crate", "sha256": "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060", "dest": "cargo/vendor", "dest-filename": "textwrap-0.11.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/textwrap-0.11.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/ucd-trie/ucd-trie-0.1.3.crate", "sha256": "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c", "dest": "cargo/vendor", "dest-filename": "ucd-trie-0.1.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2256dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/ucd-trie-0.1.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-width/unicode-width-0.1.8.crate", "sha256": "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3", "dest": "cargo/vendor", "dest-filename": "unicode-width-0.1.8.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%229337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-width-0.1.8", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-xid/unicode-xid-0.2.1.crate", "sha256": "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564", "dest": "cargo/vendor", "dest-filename": "unicode-xid-0.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-xid-0.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "shell", "dest": "cargo/vendor", "commands": [ "for c in *.crate; do tar -xf $c; done" ] }, { "type": "file", "url": "data:%5Bsource.vendored-sources%5D%0Adirectory%20%3D%20%22cargo/vendor%22%0A%0A%5Bsource.crates-io%5D%0Areplace-with%20%3D%20%22vendored-sources%22%0A", "dest": "cargo", "dest-filename": "config" } ]angelfish-v21.07/flatpak/generated-sources.json000066400000000000000000001023741407527632300216250ustar00rootroot00000000000000[ { "type": "file", "url": "https://static.crates.io/crates/adblock/adblock-0.3.15.crate", "sha256": "5e9106f026aebe0d8924a0c520e999b406629cd5b336c0b19743542ec1092134", "dest": "cargo/vendor", "dest-filename": "adblock-0.3.15.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%225e9106f026aebe0d8924a0c520e999b406629cd5b336c0b19743542ec1092134%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/adblock-0.3.15", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/addr/addr-0.14.0.crate", "sha256": "c54ccac949a2afafdfc889e15c753bbc6ee8783e026bbe3d057b00b13907db70", "dest": "cargo/vendor", "dest-filename": "addr-0.14.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c54ccac949a2afafdfc889e15c753bbc6ee8783e026bbe3d057b00b13907db70%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/addr-0.14.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/adler/adler-1.0.2.crate", "sha256": "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe", "dest": "cargo/vendor", "dest-filename": "adler-1.0.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/adler-1.0.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/aho-corasick/aho-corasick-0.7.18.crate", "sha256": "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f", "dest": "cargo/vendor", "dest-filename": "aho-corasick-0.7.18.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%221e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/aho-corasick-0.7.18", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/autocfg/autocfg-1.0.1.crate", "sha256": "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a", "dest": "cargo/vendor", "dest-filename": "autocfg-1.0.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/autocfg-1.0.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/base64/base64-0.13.0.crate", "sha256": "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd", "dest": "cargo/vendor", "dest-filename": "base64-0.13.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/base64-0.13.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/bitflags/bitflags-1.2.1.crate", "sha256": "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693", "dest": "cargo/vendor", "dest-filename": "bitflags-1.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/bitflags-1.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/byteorder/byteorder-1.4.3.crate", "sha256": "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610", "dest": "cargo/vendor", "dest-filename": "byteorder-1.4.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2214c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/byteorder-1.4.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cc/cc-1.0.68.crate", "sha256": "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787", "dest": "cargo/vendor", "dest-filename": "cc-1.0.68.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%224a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cc-1.0.68", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cfg-if/cfg-if-1.0.0.crate", "sha256": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd", "dest": "cargo/vendor", "dest-filename": "cfg-if-1.0.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cfg-if-1.0.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/codespan-reporting/codespan-reporting-0.11.1.crate", "sha256": "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e", "dest": "cargo/vendor", "dest-filename": "codespan-reporting-0.11.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%223538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/codespan-reporting-0.11.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/crc32fast/crc32fast-1.2.1.crate", "sha256": "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a", "dest": "cargo/vendor", "dest-filename": "crc32fast-1.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2281156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/crc32fast-1.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cxx/cxx-1.0.49.crate", "sha256": "65369ae12626651c9d8c5ca8b44ffdb9fe7ba710508074d2997315d2fe4f4276", "dest": "cargo/vendor", "dest-filename": "cxx-1.0.49.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2265369ae12626651c9d8c5ca8b44ffdb9fe7ba710508074d2997315d2fe4f4276%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cxx-1.0.49", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cxx-build/cxx-build-1.0.49.crate", "sha256": "8f5bde7453f995f4edcbe37c653dbc434018a4fda5941b3437005a36f69b156a", "dest": "cargo/vendor", "dest-filename": "cxx-build-1.0.49.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%228f5bde7453f995f4edcbe37c653dbc434018a4fda5941b3437005a36f69b156a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cxx-build-1.0.49", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cxxbridge-flags/cxxbridge-flags-1.0.49.crate", "sha256": "f9198d12d90c4a743fa06d45901ea419b81f250ad8f97635763cfaa791429ba1", "dest": "cargo/vendor", "dest-filename": "cxxbridge-flags-1.0.49.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f9198d12d90c4a743fa06d45901ea419b81f250ad8f97635763cfaa791429ba1%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cxxbridge-flags-1.0.49", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cxxbridge-macro/cxxbridge-macro-1.0.49.crate", "sha256": "63138f3cc0d24ef4c5a71e0def78e5375bd52d17a58cefc645b091c361dc7c3e", "dest": "cargo/vendor", "dest-filename": "cxxbridge-macro-1.0.49.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2263138f3cc0d24ef4c5a71e0def78e5375bd52d17a58cefc645b091c361dc7c3e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cxxbridge-macro-1.0.49", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/either/either-1.6.1.crate", "sha256": "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457", "dest": "cargo/vendor", "dest-filename": "either-1.6.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/either-1.6.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/flate2/flate2-1.0.20.crate", "sha256": "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0", "dest": "cargo/vendor", "dest-filename": "flate2-1.0.20.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/flate2-1.0.20", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/form_urlencoded/form_urlencoded-1.0.1.crate", "sha256": "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191", "dest": "cargo/vendor", "dest-filename": "form_urlencoded-1.0.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%225fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/form_urlencoded-1.0.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/idna/idna-0.2.3.crate", "sha256": "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8", "dest": "cargo/vendor", "dest-filename": "idna-0.2.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/idna-0.2.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/itertools/itertools-0.10.1.crate", "sha256": "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf", "dest": "cargo/vendor", "dest-filename": "itertools-0.10.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2269ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/itertools-0.10.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/lazy_static/lazy_static-1.4.0.crate", "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646", "dest": "cargo/vendor", "dest-filename": "lazy_static-1.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/lazy_static-1.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/libc/libc-0.2.98.crate", "sha256": "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790", "dest": "cargo/vendor", "dest-filename": "libc-0.2.98.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/libc-0.2.98", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/lifeguard/lifeguard-0.6.1.crate", "sha256": "89be94dbd775db37b46ca4f4bf5cf89adfb13ba197bfbcb69b2122848ee73c26", "dest": "cargo/vendor", "dest-filename": "lifeguard-0.6.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2289be94dbd775db37b46ca4f4bf5cf89adfb13ba197bfbcb69b2122848ee73c26%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/lifeguard-0.6.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/link-cplusplus/link-cplusplus-1.0.5.crate", "sha256": "8f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1", "dest": "cargo/vendor", "dest-filename": "link-cplusplus-1.0.5.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%228f1becd27d473556dc610b8afa1636ef90747b574a84553bc11e82371d5ef2d1%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/link-cplusplus-1.0.5", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/matches/matches-0.1.8.crate", "sha256": "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08", "dest": "cargo/vendor", "dest-filename": "matches-0.1.8.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%227ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/matches-0.1.8", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/memchr/memchr-2.4.0.crate", "sha256": "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc", "dest": "cargo/vendor", "dest-filename": "memchr-2.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/memchr-2.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/miniz_oxide/miniz_oxide-0.4.4.crate", "sha256": "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b", "dest": "cargo/vendor", "dest-filename": "miniz_oxide-0.4.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/miniz_oxide-0.4.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/num-traits/num-traits-0.2.14.crate", "sha256": "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290", "dest": "cargo/vendor", "dest-filename": "num-traits-0.2.14.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%229a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/num-traits-0.2.14", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/once_cell/once_cell-1.8.0.crate", "sha256": "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56", "dest": "cargo/vendor", "dest-filename": "once_cell-1.8.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/once_cell-1.8.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/percent-encoding/percent-encoding-2.1.0.crate", "sha256": "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e", "dest": "cargo/vendor", "dest-filename": "percent-encoding-2.1.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/percent-encoding-2.1.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/proc-macro2/proc-macro2-1.0.27.crate", "sha256": "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038", "dest": "cargo/vendor", "dest-filename": "proc-macro2-1.0.27.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/proc-macro2-1.0.27", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/psl/psl-2.0.37.crate", "sha256": "d3da8b9253dcf3c831625a3d49a203f8972066478f4b808cbbb5eb17ef1aec75", "dest": "cargo/vendor", "dest-filename": "psl-2.0.37.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d3da8b9253dcf3c831625a3d49a203f8972066478f4b808cbbb5eb17ef1aec75%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/psl-2.0.37", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/psl-types/psl-types-2.0.7.crate", "sha256": "66b398073e7cdd6f05934389a8f5961e3aabfa66675b6f440df4e2c793d51a4f", "dest": "cargo/vendor", "dest-filename": "psl-types-2.0.7.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2266b398073e7cdd6f05934389a8f5961e3aabfa66675b6f440df4e2c793d51a4f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/psl-types-2.0.7", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/quote/quote-1.0.9.crate", "sha256": "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7", "dest": "cargo/vendor", "dest-filename": "quote-1.0.9.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/quote-1.0.9", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/regex/regex-1.5.4.crate", "sha256": "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461", "dest": "cargo/vendor", "dest-filename": "regex-1.5.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/regex-1.5.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/regex-syntax/regex-syntax-0.6.25.crate", "sha256": "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b", "dest": "cargo/vendor", "dest-filename": "regex-syntax-0.6.25.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/regex-syntax-0.6.25", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rmp/rmp-0.8.10.crate", "sha256": "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6", "dest": "cargo/vendor", "dest-filename": "rmp-0.8.10.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%224f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rmp-0.8.10", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rmp-serde/rmp-serde-0.13.7.crate", "sha256": "011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3", "dest": "cargo/vendor", "dest-filename": "rmp-serde-0.13.7.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22011e1d58446e9fa3af7cdc1fb91295b10621d3ac4cb3a85cc86385ee9ca50cd3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rmp-serde-0.13.7", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rmp-serde/rmp-serde-0.15.5.crate", "sha256": "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d", "dest": "cargo/vendor", "dest-filename": "rmp-serde-0.15.5.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rmp-serde-0.15.5", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/scratch/scratch-1.0.0.crate", "sha256": "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69", "dest": "cargo/vendor", "dest-filename": "scratch-1.0.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%227e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/scratch-1.0.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/seahash/seahash-3.0.7.crate", "sha256": "58f57ca1d128a43733fd71d583e837b1f22239a37ebea09cde11d8d9a9080f47", "dest": "cargo/vendor", "dest-filename": "seahash-3.0.7.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2258f57ca1d128a43733fd71d583e837b1f22239a37ebea09cde11d8d9a9080f47%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/seahash-3.0.7", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde/serde-1.0.126.crate", "sha256": "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03", "dest": "cargo/vendor", "dest-filename": "serde-1.0.126.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde-1.0.126", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde_derive/serde_derive-1.0.126.crate", "sha256": "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43", "dest": "cargo/vendor", "dest-filename": "serde_derive-1.0.126.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde_derive-1.0.126", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/syn/syn-1.0.73.crate", "sha256": "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7", "dest": "cargo/vendor", "dest-filename": "syn-1.0.73.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/syn-1.0.73", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/termcolor/termcolor-1.1.2.crate", "sha256": "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4", "dest": "cargo/vendor", "dest-filename": "termcolor-1.1.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%222dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/termcolor-1.1.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/tinyvec/tinyvec-1.2.0.crate", "sha256": "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342", "dest": "cargo/vendor", "dest-filename": "tinyvec-1.2.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%225b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/tinyvec-1.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/tinyvec_macros/tinyvec_macros-0.1.0.crate", "sha256": "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c", "dest": "cargo/vendor", "dest-filename": "tinyvec_macros-0.1.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/tinyvec_macros-0.1.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/twoway/twoway-0.2.2.crate", "sha256": "c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47", "dest": "cargo/vendor", "dest-filename": "twoway-0.2.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c57ffb460d7c24cd6eda43694110189030a3d1dfe418416d9468fd1c1d290b47%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/twoway-0.2.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unchecked-index/unchecked-index-0.2.2.crate", "sha256": "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c", "dest": "cargo/vendor", "dest-filename": "unchecked-index-0.2.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unchecked-index-0.2.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-bidi/unicode-bidi-0.3.5.crate", "sha256": "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0", "dest": "cargo/vendor", "dest-filename": "unicode-bidi-0.3.5.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-bidi-0.3.5", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-normalization/unicode-normalization-0.1.19.crate", "sha256": "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9", "dest": "cargo/vendor", "dest-filename": "unicode-normalization-0.1.19.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-normalization-0.1.19", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-width/unicode-width-0.1.8.crate", "sha256": "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3", "dest": "cargo/vendor", "dest-filename": "unicode-width-0.1.8.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%229337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-width-0.1.8", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-xid/unicode-xid-0.2.2.crate", "sha256": "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3", "dest": "cargo/vendor", "dest-filename": "unicode-xid-0.2.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%228ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-xid-0.2.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/url/url-2.2.2.crate", "sha256": "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c", "dest": "cargo/vendor", "dest-filename": "url-2.2.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/url-2.2.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi/winapi-0.3.9.crate", "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419", "dest": "cargo/vendor", "dest-filename": "winapi-0.3.9.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%225c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-0.3.9", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/winapi-i686-pc-windows-gnu-0.4.0.crate", "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6", "dest": "cargo/vendor", "dest-filename": "winapi-i686-pc-windows-gnu-0.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-i686-pc-windows-gnu-0.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi-util/winapi-util-0.1.5.crate", "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178", "dest": "cargo/vendor", "dest-filename": "winapi-util-0.1.5.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2270ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-util-0.1.5", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/winapi-x86_64-pc-windows-gnu-0.4.0.crate", "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f", "dest": "cargo/vendor", "dest-filename": "winapi-x86_64-pc-windows-gnu-0.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-x86_64-pc-windows-gnu-0.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "shell", "dest": "cargo/vendor", "commands": [ "for c in *.crate; do tar -xf $c; done" ] }, { "type": "file", "url": "data:%5Bsource.vendored-sources%5D%0Adirectory%20%3D%20%22cargo/vendor%22%0A%0A%5Bsource.crates-io%5D%0Areplace-with%20%3D%20%22vendored-sources%22%0A", "dest": "cargo", "dest-filename": "config" } ]angelfish-v21.07/flatpak/regenerate-sources.sh000077500000000000000000000012431407527632300214450ustar00rootroot00000000000000#!/usr/bin/env bash set -e export GIT_CLONE_ARGS="--depth 1 --single-branch" export FLATPAK_DIR="$(readlink -f $(dirname $0))" cd ${FLATPAK_DIR} if [ ! -d flatpak-builder-tools ]; then git clone ${GIT_CLONE_ARGS} https://github.com/flatpak/flatpak-builder-tools else git -C flatpak-builder-tools pull fi if [ ! -d corrosion ]; then git clone ${GIT_CLONE_ARGS} https://github.com/AndrewGaspar/corrosion else git -C corrosion pull fi ./flatpak-builder-tools/cargo/flatpak-cargo-generator.py -o corrosion-generated-sources.json corrosion/generator/Cargo.lock ./flatpak-builder-tools/cargo/flatpak-cargo-generator.py -o generated-sources.json ../Cargo.lock angelfish-v21.07/lib/000077500000000000000000000000001407527632300144305ustar00rootroot00000000000000angelfish-v21.07/lib/CMakeLists.txt000066400000000000000000000014661407527632300171770ustar00rootroot00000000000000add_library(AngelfishCore STATIC browsermanager.cpp bookmarkshistorymodel.cpp dbmanager.cpp iconimageprovider.cpp sqlquerymodel.cpp urlutils.cpp urlobserver.cpp useragent.cpp tabsmodel.cpp settingshelper.cpp angelfishwebprofile.cpp downloadmanager.cpp qquickwebenginedownloaditem.cpp resources.qrc ) kconfig_add_kcfg_files(AngelfishCore GENERATE_MOC angelfishsettings.kcfgc) target_include_directories(AngelfishCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(AngelfishCore PRIVATE -DQT_NO_CAST_FROM_ASCII) target_link_libraries(AngelfishCore PUBLIC Qt5::Core Qt5::Sql Qt5::WebEngine KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::Notifications ) install(FILES angelfishsettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) angelfish-v21.07/lib/angelfishsettings.kcfg000066400000000000000000000102051407527632300210030ustar00rootroot00000000000000 "settingshelper.h" QStringLiteral("https://start.duckduckgo.com") QStringLiteral("https://start.duckduckgo.com/?q=") QStringLiteral("DefaultProfile") true true true true !SettingsHelper::isMobile() !SettingsHelper::isMobile() !SettingsHelper::isMobile() 300 450 true { QStringLiteral("https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt"), QStringLiteral("https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt"), QStringLiteral("https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/resource-abuse.txt"), QStringLiteral("https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/unbreak.txt"), QStringLiteral("https://easylist.to/easylist/easyprivacy.txt"), QStringLiteral("https://easylist.to/easylist/easylist.txt"), QStringLiteral("https://filters.adtidy.org/extension/ublock/filters/14.txt"), QStringLiteral("https://easylist-downloads.adblockplus.org/antiadblockfilters.txt") } { QStringLiteral("uBlock filters"), QStringLiteral("uBlock filters – Privacy"), QStringLiteral("uBlock filters – Resource abuse"), QStringLiteral("uBlock filters – Unbreak"), QStringLiteral("EasyPrivacy"), QStringLiteral("Easylist"), QStringLiteral("AdGuard Annoyances"), QStringLiteral("Adblock Warning Removal List") } angelfish-v21.07/lib/angelfishsettings.kcfgc000066400000000000000000000004351407527632300211520ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2020 Jonah Brüchert # # SPDX-License-Identifier: LGPL-2.0-or-later File=angelfishsettings.kcfg ClassName=AngelfishSettings Mutators=true DefaultValueGetters=true GenerateProperties=true ParentInConstructor=true Singleton=true Notifiers=true angelfish-v21.07/lib/angelfishwebprofile.cpp000066400000000000000000000067551407527632300211700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "angelfishwebprofile.h" #include #include #include #include #include #include #include "downloadmanager.h" #include "qquickwebenginedownloaditem.h" AngelfishWebProfile::AngelfishWebProfile(QObject *parent) : QQuickWebEngineProfile(parent) , m_questionLoader(nullptr) , m_urlInterceptor(nullptr) { connect(this, &QQuickWebEngineProfile::downloadRequested, this, &AngelfishWebProfile::handleDownload); connect(this, &QQuickWebEngineProfile::downloadFinished, this, &AngelfishWebProfile::handleDownloadFinished); connect(this, &QQuickWebEngineProfile::presentNotification, this, &AngelfishWebProfile::showNotification); } void AngelfishWebProfile::handleDownload(QQuickWebEngineDownloadItem *downloadItem) { // if we don't accept the request right away, it will be deleted downloadItem->accept(); // therefore just stop the download again as quickly as possible, // and ask the user for confirmation downloadItem->pause(); DownloadManager::instance().addDownload(downloadItem); if (m_questionLoader) { m_questionLoader->setProperty("source", QStringLiteral("qrc:/DownloadQuestion.qml")); if (auto *question = m_questionLoader->property("item").value()) { question->setProperty("download", QVariant::fromValue(downloadItem)); question->setVisible(true); } } } void AngelfishWebProfile::handleDownloadFinished(QQuickWebEngineDownloadItem *downloadItem) { QQuickWindow *window = m_questionLoader->window(); const auto passiveNotification = [window](const QString &text) { QMetaObject::invokeMethod(window, "showPassiveNotification", Q_ARG(QVariant, text), Q_ARG(QVariant, {}), Q_ARG(QVariant, {}), Q_ARG(QVariant, {})); }; switch (downloadItem->state()) { case QQuickWebEngineDownloadItem::DownloadCompleted: qDebug() << "download finished"; passiveNotification(i18n("Download finished")); break; case QQuickWebEngineDownloadItem::DownloadInterrupted: qDebug() << "Download failed"; passiveNotification(i18n("Download failed")); qDebug() << "Download interrupt reason:" << downloadItem->interruptReasonString(); break; case QQuickWebEngineDownloadItem::DownloadCancelled: qDebug() << "Download cancelled by the user"; break; default: break; } } void AngelfishWebProfile::showNotification(QWebEngineNotification *webNotification) { auto *notification = new KNotification(QStringLiteral("web-notification")); notification->setTitle(webNotification->title()); notification->setText(webNotification->message()); notification->setPixmap(QPixmap::fromImage(webNotification->icon())); connect(notification, &KNotification::closed, webNotification, &QWebEngineNotification::close); connect(notification, &KNotification::defaultActivated, webNotification, &QWebEngineNotification::click); notification->sendEvent(); } QWebEngineUrlRequestInterceptor *AngelfishWebProfile::urlInterceptor() const { return m_urlInterceptor; } void AngelfishWebProfile::setUrlInterceptor(QWebEngineUrlRequestInterceptor *urlRequestInterceptor) { setUrlRequestInterceptor(urlRequestInterceptor); m_urlInterceptor = urlRequestInterceptor; Q_EMIT urlInterceptorChanged(); } angelfish-v21.07/lib/angelfishwebprofile.h000066400000000000000000000024571407527632300206300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include class QWebEngineNotification; class QQuickItem; class QWebEngineUrlRequestInterceptor; class AngelfishWebProfile : public QQuickWebEngineProfile { Q_OBJECT Q_PROPERTY(QQuickItem *questionLoader MEMBER m_questionLoader NOTIFY questionLoaderChanged) Q_PROPERTY(QWebEngineUrlRequestInterceptor *urlInterceptor WRITE setUrlInterceptor READ urlInterceptor NOTIFY urlInterceptorChanged) public: explicit AngelfishWebProfile(QObject *parent = nullptr); Q_SIGNAL void questionLoaderChanged(); Q_SIGNAL void urlInterceptorChanged(); QWebEngineUrlRequestInterceptor *urlInterceptor() const; void setUrlInterceptor(QWebEngineUrlRequestInterceptor *urlRequestInterceptor); private: void handleDownload(QQuickWebEngineDownloadItem *downloadItem); void handleDownloadFinished(QQuickWebEngineDownloadItem *downloadItem); void showNotification(QWebEngineNotification *webNotification); QQuickItem *m_questionLoader; // A valid property needs a read function, and there is no getter in QQuickWebEngineProfile // so store a pointer ourselves QWebEngineUrlRequestInterceptor *m_urlInterceptor; }; angelfish-v21.07/lib/bookmarkshistorymodel.cpp000066400000000000000000000060771407527632300216010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "bookmarkshistorymodel.h" #include "browsermanager.h" #include #include #include #include constexpr int QUERY_LIMIT = 1000; BookmarksHistoryModel::BookmarksHistoryModel(QObject *parent) : SqlQueryModel(parent) { connect(BrowserManager::instance(), &BrowserManager::databaseTableChanged, this, &BookmarksHistoryModel::onDatabaseChanged); } void BookmarksHistoryModel::setActive(bool a) { if (m_active == a) return; m_active = a; if (m_active) setQuery(); else clear(); emit activeChanged(); } void BookmarksHistoryModel::setBookmarks(bool b) { if (m_bookmarks == b) return; m_bookmarks = b; setQuery(); emit bookmarksChanged(); } void BookmarksHistoryModel::setHistory(bool h) { if (m_history == h) return; m_history = h; setQuery(); emit historyChanged(); } void BookmarksHistoryModel::setFilter(const QString &f) { if (m_filter == f) return; m_filter = f; setQuery(); emit filterChanged(); } void BookmarksHistoryModel::onDatabaseChanged(const QString &table) { if ((table == QLatin1String("bookmarks") && m_bookmarks) || (table == QLatin1String("history") && m_history)) setQuery(); } void BookmarksHistoryModel::setQuery() { if (!m_active) return; QString command; const auto b = QStringLiteral(u"SELECT rowid AS id, url, title, icon, :now - lastVisited AS lastVisitedDelta, %1 AS bookmarked FROM %2 "); const QStringView filter = m_filter.isEmpty() ? QStringView() : QStringView(u"WHERE url LIKE '%' || :filter || '%' OR title LIKE '%' || :filter || '%'"); const bool includeHistory = m_history && !(m_bookmarks && m_filter.isEmpty()); if (m_bookmarks) { command = b.arg(1).arg(QStringView(u"bookmarks")); command += filter; } if (m_bookmarks && includeHistory) { command += QStringView(u"\n UNION \n"); } if (includeHistory) { command += b.arg(0).arg(QStringView(u"history")); command += filter; } command += QStringView(u"\n ORDER BY bookmarked DESC, lastVisitedDelta ASC"); if (includeHistory) { command += QStringLiteral(u"\n LIMIT %1").arg(QUERY_LIMIT); } const qint64 ref = QDateTime::currentSecsSinceEpoch(); QSqlQuery query; if (!query.prepare(command)) { qWarning() << Q_FUNC_INFO << "Failed to prepare SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return; } if (!m_filter.isEmpty()) query.bindValue(QStringLiteral(":filter"), m_filter); query.bindValue(QStringLiteral(":now"), ref); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return; } SqlQueryModel::setQuery(query); } angelfish-v21.07/lib/bookmarkshistorymodel.h000066400000000000000000000035011407527632300212330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef BOOKMARKSHISTORYMODEL_H #define BOOKMARKSHISTORYMODEL_H #include "sqlquerymodel.h" /** * @class BookmarksHistoryModel * @short Model for listing Bookmarks and History items. */ class BookmarksHistoryModel : public SqlQueryModel { Q_OBJECT // while active, data is shown and changes in the used database table(s) // will trigger new query Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) // set to true for including bookmarks Q_PROPERTY(bool bookmarks READ bookmarks WRITE setBookmarks NOTIFY bookmarksChanged) // set to true for including history Q_PROPERTY(bool history READ history WRITE setHistory NOTIFY historyChanged) // set to string to filter url or title by it. without filter set, only // bookmarks are shown Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged) public: BookmarksHistoryModel(QObject *parent = nullptr); bool active() const { return m_active; } void setActive(bool a); bool bookmarks() const { return m_bookmarks; } void setBookmarks(bool b); bool history() const { return m_history; } void setHistory(bool h); QString filter() const { return m_filter; } void setFilter(const QString &f); signals: void activeChanged(); void bookmarksChanged(); void historyChanged(); void filterChanged(); private: void onDatabaseChanged(const QString &table); void setQuery(); private: bool m_active = true; bool m_bookmarks = false; bool m_history = false; QString m_filter; }; #endif // BOOKMARKSHISTORYMODEL_H angelfish-v21.07/lib/browsermanager.cpp000066400000000000000000000033601407527632300201540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later #include "browsermanager.h" #include #include #include #include "angelfishsettings.h" BrowserManager *BrowserManager::s_instance = nullptr; BrowserManager::BrowserManager(QObject *parent) : QObject(parent) , m_dbmanager(new DBManager(this)) { connect(m_dbmanager, &DBManager::databaseTableChanged, this, &BrowserManager::databaseTableChanged); } void BrowserManager::addBookmark(const QVariantMap &bookmarkdata) { qDebug() << "Add bookmark"; qDebug() << " data: " << bookmarkdata; m_dbmanager->addBookmark(bookmarkdata); } void BrowserManager::removeBookmark(const QString &url) { m_dbmanager->removeBookmark(url); } bool BrowserManager::isBookmarked(const QString &url) const { return m_dbmanager->isBookmarked(url); } void BrowserManager::addToHistory(const QVariantMap &pagedata) { // qDebug() << "Add History"; // qDebug() << " data: " << pagedata; m_dbmanager->addToHistory(pagedata); } void BrowserManager::removeFromHistory(const QString &url) { m_dbmanager->removeFromHistory(url); } void BrowserManager::updateLastVisited(const QString &url) { m_dbmanager->updateLastVisited(url); } void BrowserManager::updateIcon(const QString &url, const QString &iconSource) { m_dbmanager->updateIcon(url, iconSource); } QString BrowserManager::initialUrl() const { return m_initialUrl; } void BrowserManager::setInitialUrl(const QString &initialUrl) { m_initialUrl = initialUrl; Q_EMIT initialUrlChanged(); } BrowserManager *BrowserManager::instance() { if (!s_instance) s_instance = new BrowserManager(); return s_instance; } angelfish-v21.07/lib/browsermanager.h000066400000000000000000000026571407527632300176310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef BOOKMARKSMANAGER_H #define BOOKMARKSMANAGER_H #include #include "dbmanager.h" class QSettings; /** * @class BookmarksManager * @short Access to Bookmarks and History. This is a singleton for * administration and access to the various models and browser-internal * data. */ class BrowserManager : public QObject { Q_OBJECT Q_PROPERTY(QString initialUrl READ initialUrl WRITE setInitialUrl NOTIFY initialUrlChanged) public: static BrowserManager *instance(); QString initialUrl() const; void setInitialUrl(const QString &initialUrl); bool isBookmarked(const QString &url) const; signals: void updated(); void initialUrlChanged(); void databaseTableChanged(QString table); public slots: void addBookmark(const QVariantMap &bookmarkdata); void removeBookmark(const QString &url); void addToHistory(const QVariantMap &pagedata); void removeFromHistory(const QString &url); void updateLastVisited(const QString &url); void updateIcon(const QString &url, const QString &iconSource); private: // BrowserManager should only be createdd by calling the instance() function BrowserManager(QObject *parent = nullptr); DBManager *m_dbmanager; QString m_initialUrl; static BrowserManager *s_instance; }; #endif // BOOKMARKSMANAGER_H angelfish-v21.07/lib/contents/000077500000000000000000000000001407527632300162655ustar00rootroot00000000000000angelfish-v21.07/lib/contents/ui/000077500000000000000000000000001407527632300167025ustar00rootroot00000000000000angelfish-v21.07/lib/contents/ui/AuthSheet.qml000066400000000000000000000030571407527632300213140ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 as Controls import org.kde.kirigami 2.5 as Kirigami import QtWebEngine 1.4 Kirigami.OverlaySheet { id: authSheet property AuthenticationDialogRequest request header: Kirigami.Heading { elide: Text.ElideRight wrapMode: Text.WordWrap Layout.fillWidth: true text: i18n("Authentication required") } Kirigami.FormLayout { Layout.fillWidth: true Controls.TextField { id: usernameField Kirigami.FormData.label: i18n("Username") Layout.fillWidth: true } Controls.TextField { id: passwordField echoMode: TextInput.Password Kirigami.FormData.label: i18n("Password") Layout.fillWidth: true } RowLayout { Layout.fillWidth: true Controls.Button { Layout.fillWidth: true text: i18n("Accept") onClicked: { authSheet.request.dialogAccept(usernameField.text, passwordField.text) authSheet.close() } } Controls.Button { Layout.fillWidth: true text: i18n("Cancel") onClicked: { authSheet.request.dialogReject() authSheet.close() } } } } } angelfish-v21.07/lib/contents/ui/DownloadQuestion.qml000066400000000000000000000015731407527632300227220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.0 import org.kde.kirigami 2.4 as Kirigami import QtWebEngine 1.1 Kirigami.InlineMessage { id: downloadQuestion text: i18n("Do you want to download this file?") showCloseButton: false property WebEngineDownloadItem download actions: [ Kirigami.Action { iconName: "download" text: i18n("Download") onTriggered: { downloadQuestion.download.resume() downloadQuestion.visible = false } }, Kirigami.Action { icon.name: "dialog-cancel" text: i18n("Cancel") onTriggered: { downloadQuestion.download.cancel() downloadQuestion.visible = false } } ] } angelfish-v21.07/lib/contents/ui/ErrorHandler.qml000066400000000000000000000025351407527632300220110ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.0 import org.kde.kirigami 2.0 as Kirigami Item { id: errorHandler signal refreshRequested property string errorCode: "" property alias errorString: errorDescription.text Behavior on height { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad} } Rectangle { anchors.fill: parent; color: Kirigami.Theme.viewBackgroundColor; } ColumnLayout { spacing: Kirigami.Units.gridUnit anchors { fill: parent margins: Kirigami.Units.gridUnit } Kirigami.Heading { opacity: 0.3 text: errorCode } Kirigami.Heading { level: 3 Layout.fillHeight: false text: i18n("Error loading the page") } Controls.Label { id: errorDescription Layout.fillHeight: false } Item { Layout.fillHeight: true } Controls.ToolButton { Layout.alignment: Qt.AlignHCenter text: i18n("Retry") icon.name: "view-refresh" onClicked: errorHandler.refreshRequested() } } } angelfish-v21.07/lib/contents/ui/JavaScriptDialogSheet.qml000066400000000000000000000101131407527632300235700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import org.kde.kirigami 2.7 as Kirigami import QtQuick.Layouts 1.2 import QtWebEngine 1.4 Kirigami.OverlaySheet { id: dialogSheet property JavaScriptDialogRequest request Item { Component { id: alert Controls.Button { text: i18n("Close") onClicked: { dialogSheet.request.dialogReject() dialogSheet.close() } } } Component { id: confirm RowLayout { Controls.Button { Layout.fillWidth: true text: i18n("Confirm") onClicked: { dialogSheet.request.dialogAccept() dialogSheet.close() } } Controls.Button { Layout.fillWidth: true text: i18n("Cancel") onClicked: { dialogSheet.request.dialogReject() dialogSheet.close() } } } } Component { id: prompt ColumnLayout { Controls.TextField { id: inputField Layout.fillWidth: true text: dialogSheet.request.defaultText } RowLayout { Layout.fillWidth: true Controls.Button { Layout.fillWidth: true text: i18n("Cancel") onClicked: { dialogSheet.request.dialogReject() dialogSheet.close() } } Controls.Button { Layout.fillWidth: true text: i18n("Submit") onClicked: { dialogSheet.request.dialogAccept(inputField.text) dialogSheet.close() } } } } } Component { id: beforeUnload Controls.Button { text: i18n("Leave page") onClicked: { dialogSheet.request.dialogAccept() dialogSheet.close() } } } } onSheetOpenChanged: { if (dialogSheet.sheetOpen) { switch(request.type) { case JavaScriptDialogRequest.DialogTypeAlert: controlLoader.sourceComponent = alert break case JavaScriptDialogRequest.DialogTypeConfirm: controlLoader.sourceComponent = confirm break case JavaScriptDialogRequest.DialogTypePrompt: controlLoader.sourceComponent = prompt break case JavaScriptDialogRequest.DialogTypeBeforeUnload: controlLoader.sourceComponent = beforeUnload break } } else { dialogSheet.request.dialogReject() } } ColumnLayout { Controls.Label { visible: text text: { if (request) { if (request.message) { return request.message } if (request.type == JavaScriptDialogRequest.DialogTypeBeforeUnload) { return i18n("The website asks for confirmation that you want to leave. Unsaved information might not be saved.") } } return "" } Layout.fillWidth: true Layout.fillHeight: true wrapMode: Text.WordWrap } Loader { Layout.fillWidth: true id: controlLoader } } } angelfish-v21.07/lib/contents/ui/ListWebView.qml000066400000000000000000000040731407527632300216250ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQml.Models 2.1 import QtWebEngine 1.6 import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Repeater { id: tabs clip: true property bool activeTabs: false property bool privateTabsMode: false property alias currentIndex: tabsModel.currentTab property WebView currentItem property alias tabsModel: tabsModel property WebEngineProfile profile: AngelfishWebProfile { httpUserAgent: tabs.currentItem.userAgent.userAgent offTheRecord: tabs.privateTabsMode storageName: tabs.privateTabsMode ? "Private" : Settings.profile questionLoader: rootPage.questionLoader urlInterceptor: typeof AdblockUrlInterceptor !== "undefined" && AdblockUrlInterceptor } model: TabsModel { id: tabsModel isMobileDefault: Kirigami.Settings.isMobile privateMode: privateTabsMode } delegate: WebView { id: webView anchors { bottom: tabs.bottom top: tabs.top } privateMode: tabs.privateTabsMode userAgent.isMobile: model.isMobile width: tabs.width profile: tabs.profile Component.onCompleted: { url = model.pageurl } property bool readyForSnapshot: false property bool showView: index === tabs.currentIndex visible: (showView || readyForSnapshot || loadingActive) && tabs.activeTabs x: showView && tabs.activeTabs ? 0 : -width z: showView && tabs.activeTabs ? 0 : -1 onShowViewChanged: { if (showView) { tabs.currentItem = webView } } onRequestedUrlChanged: tabsModel.setUrl(index, requestedUrl) Connections { target: webView.userAgent function onUserAgentChanged() { tabsModel.setIsMobile(index, webView.userAgent.isMobile); } } } } angelfish-v21.07/lib/contents/ui/PermissionQuestion.qml000066400000000000000000000041241407527632300232760ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import org.kde.kirigami 2.4 as Kirigami import QtWebEngine 1.9 Kirigami.InlineMessage { property int permission property string origin id: permissionQuestion text: { switch(permission) { case WebEngineView.Geolocation: i18n("Do you want to allow the website to access the geo location?") break case WebEngineView.MediaAudioCapture: i18n("Do you want to allow the website to access the microphone?") break case WebEngineView.MediaVideoCapture: i18n("Do you want to allow the website to access the camera?") break case WebEngineView.MediaAudioVideoCapture: i18n("Do you want to allow the website to access the camera and the microphone?") break case WebEngineView.DesktopVideoCapture: i18n("Do you want to allow the website to share your screen?") break case WebEngineView.DesktopAudioVideoCapture: i18n("Do you want to allow the website to share the sound output?") break case WebEngineView.Notifications: i18n("Do you want to allow the website to send you notifications?") break } } showCloseButton: false actions: [ Kirigami.Action { icon.name: "dialog-ok-apply" text: i18n("Accept") onTriggered: { currentWebView.grantFeaturePermission( permissionQuestion.origin, permissionQuestion.permission, true) permissionQuestion.visible = false } }, Kirigami.Action { icon.name: "dialog-cancel" text: i18n("Decline") onTriggered: { currentWebView.grantFeaturePermission( permissionQuestion.origin, permissionQuestion.permission, false) permissionQuestion.visible = false } } ] } angelfish-v21.07/lib/contents/ui/WebView.qml000066400000000000000000000231161407527632300207700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Window 2.1 import QtQuick.Layouts 1.3 import QtWebEngine 1.10 import org.kde.kirigami 2.4 as Kirigami import org.kde.angelfish 1.0 WebEngineView { id: webEngineView property string errorCode: "" property string errorString: "" property bool privateMode: false property alias userAgent: userAgent // loadingActive property is set to true when loading is started // and turned to false only after succesful or failed loading. It // is possible to set it to false by calling stopLoading method. // // The property was introduced as it triggers visibility of the webEngineView // in the other parts of the code. When using loading that is linked // to visibility, stop/start loading was observed in some conditions. It looked as if // there is an internal optimization of webengine in the case of parallel // loading of several pages that could use visibility as one of the decision // making parameters. property bool loadingActive: false // reloadOnVisible property ensures that the view has been always // loaded at least once while it is visible. When the view is loaded // while visible is set to false, there, what appears to be Chromium // optimizations that can disturb the loading. property bool reloadOnVisible: true // Profiles of WebViews are shared among all views of the same type (regular or // private). However, within each group of tabs, we can have some tabs that are // using mobile or desktop user agent. To avoid loading a page with the wrong // user agent, the agent is checked in the beginning of the loading at onLoadingChanged // handler. If the user agent is wrong, loading is stopped and reloadOnMatchingAgents // property is set to true. As soon as the agent is correct, the page is loaded. property bool reloadOnMatchingAgents: false // Used to follow whether agents match property bool agentsMatch: profile.httpUserAgent === userAgent.userAgent // URL that was requested and should be used // as a base for user interaction. It reflects // last request (successful or failed) property url requestedUrl: url property int findInPageResultIndex property int findInPageResultCount // Menu that can be overriden from subclasses property Controls.Menu contextMenu: Controls.Menu { property ContextMenuRequest request Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanCopy) != 0 text: i18n("Copy") onTriggered: webEngineView.triggerWebAction(WebEngineView.Copy) } Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanCut) != 0 text: i18n("Cut") onTriggered: webEngineView.triggerWebAction(WebEngineView.Cut) } Controls.MenuItem { enabled: contextMenu.request && (contextMenu.request.editFlags & ContextMenuRequest.CanPaste) != 0 text: i18n("Paste") onTriggered: webEngineView.triggerWebAction(WebEngineView.Paste) } Controls.MenuItem { enabled: contextMenu.request && contextMenu.request.selectedText text: contextMenu.request && contextMenu.request.selectedText ? i18n("Search online for '%1'", contextMenu.request.selectedText) : i18n("Search online") onTriggered: tabsModel.newTab(UrlUtils.urlFromUserInput(Settings.searchBaseUrl + contextMenu.request.selectedText)); } Controls.MenuItem { enabled: contextMenu.request && contextMenu.request.linkUrl !== "" text: i18n("Copy Url") onTriggered: webEngineView.triggerWebAction(WebEngineView.CopyLinkToClipboard) } Controls.MenuItem { text: i18n("View source") onTriggered: tabsModel.newTab("view-source:" + webEngineView.url) } Controls.MenuItem { text: i18n("Download") onTriggered: webEngineView.triggerWebAction(WebEngineView.DownloadLinkToDisk) } Controls.MenuItem { enabled: contextMenu.request && contextMenu.request.linkUrl !== "" text: i18n("Open in new Tab") onTriggered: webEngineView.triggerWebAction(WebEngineView.OpenLinkInNewTab) } } UserAgentGenerator { id: userAgent onUserAgentChanged: webEngineView.reload() } settings { autoLoadImages: Settings.webAutoLoadImages javascriptEnabled: Settings.webJavaScriptEnabled // Disable builtin error pages in favor of our own errorPageEnabled: false // Load larger touch icons touchIconsEnabled: true // Disable scrollbars on mobile showScrollBars: false } focus: true onLoadingChanged: { //print("Loading: " + loading); print(" url: " + loadRequest.url + " " + loadRequest.status) //print(" icon: " + webEngineView.icon) //print(" title: " + webEngineView.title) /* Handle * - WebEngineView::LoadStartedStatus, * - WebEngineView::LoadStoppedStatus, * - WebEngineView::LoadSucceededStatus and * - WebEngineView::LoadFailedStatus */ var ec = ""; var es = ""; if (loadRequest.status === WebEngineView.LoadStartedStatus) { if (profile.httpUserAgent !== userAgent.userAgent) { //print("Mismatch of user agents, will load later " + loadRequest.url); reloadOnMatchingAgents = true; stopLoading(); } else { loadingActive = true; } } if (loadRequest.status === WebEngineView.LoadSucceededStatus) { if (!privateMode) { const request = { url: currentWebView.url, title: currentWebView.title, icon: currentWebView.icon } BrowserManager.addToHistory(request); BrowserManager.updateLastVisited(currentWebView.url); } loadingActive = false; } if (loadRequest.status === WebEngineView.LoadFailedStatus) { print("Load failed: " + loadRequest.errorCode + " " + loadRequest.errorString); print("Load failed url: " + loadRequest.url + " " + url); ec = loadRequest.errorCode; es = loadRequest.errorString; loadingActive = false; // update requested URL only after its clear that it fails. // Otherwise, its updated as a part of url property update. if (requestedUrl !== loadRequest.url) requestedUrl = loadRequest.url; } errorCode = ec; errorString = es; } Component.onCompleted: { print("WebView completed."); print("Settings: " + webEngineView.settings); } onIconChanged: { if (icon && !privateMode) BrowserManager.updateIcon(url, icon) } onNewViewRequested: { if (request.userInitiated) { tabsModel.newTab(request.requestedUrl.toString()) showPassiveNotification(i18n("Website was opened in a new tab")) } else { questionLoader.setSource("NewTabQuestion.qml") questionLoader.item.url = request.requestedUrl questionLoader.item.visible = true } } onUrlChanged: { if (requestedUrl !== url) { requestedUrl = url; } } onFullScreenRequested: { request.accept() if (webBrowser.visibility !== Window.FullScreen) webBrowser.showFullScreen() else webBrowser.showNormal() } onContextMenuRequested: { request.accepted = true // Make sure QtWebEngine doesn't show its own context menu. contextMenu.request = request contextMenu.x = request.x contextMenu.y = request.y contextMenu.open() } onAuthenticationDialogRequested: { request.accepted = true sheetLoader.setSource("AuthSheet.qml") sheetLoader.item.request = request sheetLoader.item.open() } onFeaturePermissionRequested: { questionLoader.setSource("PermissionQuestion.qml") questionLoader.item.permission = feature questionLoader.item.origin = securityOrigin questionLoader.item.visible = true } onJavaScriptDialogRequested: { request.accepted = true sheetLoader.setSource("JavaScriptDialogSheet.qml") sheetLoader.item.request = request sheetLoader.item.open() } onFindTextFinished: { findInPageResultIndex = result.activeMatch; findInPageResultCount = result.numberOfMatches; } onVisibleChanged: { if (visible && reloadOnVisible) { // see description of reloadOnVisible above for reasoning reloadOnVisible = false; reload(); } } onAgentsMatchChanged: { if (agentsMatch && reloadOnMatchingAgents) { // see description of reloadOnMatchingAgents above for reasoning reloadOnMatchingAgents = false; reload(); } } function findInPageBack(text) { findText(text, WebEngineView.FindBackward); } function findInPageForward(text) { findText(text); } function stopLoading() { loadingActive = false; stop(); } } angelfish-v21.07/lib/dbmanager.cpp000066400000000000000000000177131407527632300170650ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "dbmanager.h" #include "iconimageprovider.h" #include #include #include #include #include #include #include #include #include constexpr int DB_USER_VERSION = 1; constexpr int MAX_BROWSER_HISTORY_SIZE = 3000; DBManager::DBManager(QObject *parent) : QObject(parent) { const QString dbpath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); const QString dbname = dbpath + QStringLiteral("/angelfish.sqlite"); if (!QDir().mkpath(dbpath)) { qCritical() << "Database directory does not exist and cannot be created: " << dbpath; throw std::runtime_error("Database directory does not exist and cannot be created: " + dbpath.toStdString()); } QSqlDatabase database = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); database.setDatabaseName(dbname); if (!database.open()) { qCritical() << "Failed to open database" << dbname; throw std::runtime_error("Failed to open database " + dbname.toStdString()); } if (!migrate()) { qCritical() << "Failed to initialize or migrate the schema in" << dbname; throw std::runtime_error("Failed to initialize or migrate the schema in " + dbname.toStdString()); } trimHistory(); trimIcons(); } int DBManager::version() { QSqlQuery query(QStringLiteral("PRAGMA user_version")); if (query.next()) { bool ok = false; int value = query.value(0).toInt(&ok); if (ok) return value; } return -1; } void DBManager::setVersion(int v) { QSqlQuery query; query.prepare(QStringLiteral("PRAGMA user_version = %1").arg(v)); query.exec(); } bool DBManager::execute(const QString &command) { QSqlQuery query; if (!query.exec(command)) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return false; } return true; } bool DBManager::execute(QSqlQuery &query) { if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return false; } return true; } bool DBManager::migrate() { for (int v = version(); v != DB_USER_VERSION; v = version()) { if (v < 0 || v > DB_USER_VERSION) { qCritical() << "Don't know what to do with the database schema version" << v << ". Bailing out."; return false; } if (v == 0) { if (!migrateTo1()) return false; } } return true; } bool DBManager::migrateTo1() { // Starting from empty database, let's create the tables. const QString bookmarks = QStringLiteral("CREATE TABLE bookmarks (url TEXT UNIQUE, title TEXT, icon TEXT, lastVisited INT)"); const QString history = QStringLiteral("CREATE TABLE history (url TEXT UNIQUE, title TEXT, icon TEXT, lastVisited INT)"); const QString icons = QStringLiteral("CREATE TABLE icons (url TEXT UNIQUE, icon BLOB)"); const QString idx_bookmarks = QStringLiteral("CREATE UNIQUE INDEX idx_bookmarks_url ON bookmarks(url)"); const QString idx_history = QStringLiteral("CREATE UNIQUE INDEX idx_history_url ON history(url)"); const QString idx_icons = QStringLiteral("CREATE UNIQUE INDEX idx_icons_url ON icons(url)"); if (!execute(bookmarks) || !execute(idx_bookmarks) || !execute(history) || !execute(idx_history) || !execute(icons) || !execute(idx_icons)) return false; setVersion(1); qDebug() << "Migrated database schema to version 1"; return true; } void DBManager::trimHistory() { execute(QStringLiteral("DELETE FROM history WHERE rowid NOT IN (SELECT rowid FROM history" " ORDER BY lastVisited DESC LIMIT %1)") .arg(MAX_BROWSER_HISTORY_SIZE)); } void DBManager::trimIcons() { execute( QStringLiteral("DELETE FROM icons WHERE url NOT IN " "(SELECT icon FROM history UNION SELECT icon FROM bookmarks)")); } void DBManager::addRecord(const QString &table, const QVariantMap &pagedata) { const QString url = pagedata.value(QStringLiteral("url")).toString(); const QString title = pagedata.value(QStringLiteral("title")).toString(); const QString icon = pagedata.value(QStringLiteral("icon")).toString(); const qint64 lastVisited = QDateTime::currentSecsSinceEpoch(); if (url.isEmpty() || url == QStringLiteral("about:blank")) return; QSqlQuery query; query.prepare(QStringLiteral("INSERT OR REPLACE INTO %1 (url, title, icon, lastVisited) " "VALUES (:url, :title, :icon, :lastVisited)") .arg(table)); query.bindValue(QStringLiteral(":url"), url); query.bindValue(QStringLiteral(":title"), title); query.bindValue(QStringLiteral(":icon"), icon); query.bindValue(QStringLiteral(":lastVisited"), lastVisited); execute(query); emit databaseTableChanged(table); } void DBManager::removeRecord(const QString &table, const QString &url) { if (url.isEmpty()) return; QSqlQuery query; query.prepare(QStringLiteral("DELETE FROM %1 WHERE url = :url").arg(table)); query.bindValue(QStringLiteral(":url"), url); execute(query); emit databaseTableChanged(table); } bool DBManager::hasRecord(const QString &table, const QString &url) const { QSqlQuery query; query.prepare(QStringLiteral("SELECT 1 FROM %1 WHERE url = :url").arg(table)); query.bindValue(QStringLiteral(":url"), url); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return false; } while (query.next()) { return true; } return false; } void DBManager::updateIconRecord(const QString &table, const QString &url, const QString &iconSource) { if (url.isEmpty()) return; QSqlQuery query; query.prepare(QStringLiteral("UPDATE %1 SET icon = :icon WHERE url = :url").arg(table)); query.bindValue(QStringLiteral(":url"), url); query.bindValue(QStringLiteral(":icon"), iconSource); execute(query); emit databaseTableChanged(table); } void DBManager::setLastVisitedRecord(const QString &table, const QString &url) { if (url.isEmpty()) return; const qint64 lastVisited = QDateTime::currentSecsSinceEpoch(); QSqlQuery query; query.prepare(QStringLiteral("UPDATE %1 SET lastVisited = :lv WHERE url = :url").arg(table)); query.bindValue(QStringLiteral(":url"), url); query.bindValue(QStringLiteral(":lv"), lastVisited); execute(query); emit databaseTableChanged(table); } void DBManager::addBookmark(const QVariantMap &bookmarkdata) { addRecord(QStringLiteral("bookmarks"), bookmarkdata); } void DBManager::removeBookmark(const QString &url) { removeRecord(QStringLiteral("bookmarks"), url); } bool DBManager::isBookmarked(const QString &url) const { return hasRecord(QStringLiteral("bookmarks"), url); } void DBManager::addToHistory(const QVariantMap &pagedata) { addRecord(QStringLiteral("history"), pagedata); } void DBManager::removeFromHistory(const QString &url) { removeRecord(QStringLiteral("history"), url); } void DBManager::updateLastVisited(const QString &url) { setLastVisitedRecord(QStringLiteral("bookmarks"), url); setLastVisitedRecord(QStringLiteral("history"), url); } void DBManager::updateIcon(const QString &url, const QString &iconSource) { const QString updatedSource = IconImageProvider::storeImage(iconSource); updateIconRecord(QStringLiteral("bookmarks"), url, updatedSource); updateIconRecord(QStringLiteral("history"), url, updatedSource); } angelfish-v21.07/lib/dbmanager.h000066400000000000000000000034621407527632300165260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef DBMANAGER_H #define DBMANAGER_H #include #include class QSqlQuery; /** * @class DBManager * @short Class for database initialization and applying changes in its records */ class DBManager : public QObject { Q_OBJECT public: explicit DBManager(QObject *parent = nullptr); signals: // emitted with the name of the table that has been changed void databaseTableChanged(QString table); public: void addBookmark(const QVariantMap &bookmarkdata); void removeBookmark(const QString &url); bool isBookmarked(const QString &url) const; void addToHistory(const QVariantMap &pagedata); void removeFromHistory(const QString &url); void updateIcon(const QString &url, const QString &iconSource); void updateLastVisited(const QString &url); private: // version of database schema int version(); void setVersion(int v); // migration from earlier versions bool migrate(); bool migrateTo1(); // limit the size of history table void trimHistory(); // drop unused icons void trimIcons(); // execute SQL statement bool execute(const QString &command); bool execute(QSqlQuery &query); // methods for manipulation of bookmarks or history tables void addRecord(const QString &table, const QVariantMap &pagedata); void removeRecord(const QString &table, const QString &url); void updateIconRecord(const QString &table, const QString &url, const QString &iconSource); void setLastVisitedRecord(const QString &table, const QString &url); bool hasRecord(const QString &table, const QString &url) const; }; #endif // DBMANAGER_H angelfish-v21.07/lib/downloadmanager.cpp000066400000000000000000000013001407527632300202700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "downloadmanager.h" #include #include "qquickwebenginedownloaditem.h" DownloadManager::DownloadManager() = default; DownloadManager &DownloadManager::instance() { static DownloadManager instance; return instance; } void DownloadManager::addDownload(QQuickWebEngineDownloadItem *download) { m_downloads.push_back(download); } void DownloadManager::removeDownload(const int index) { m_downloads.at(index)->cancel(); m_downloads.removeAt(index); } const QVector &DownloadManager::downloads() { return m_downloads; } angelfish-v21.07/lib/downloadmanager.h000066400000000000000000000010331407527632300177400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include class QQuickWebEngineDownloadItem; class DownloadManager { public: static DownloadManager &instance(); Q_INVOKABLE void addDownload(QQuickWebEngineDownloadItem *download); Q_INVOKABLE void removeDownload(const int index); const QVector &downloads(); private: DownloadManager(); QVector m_downloads; }; angelfish-v21.07/lib/iconimageprovider.cpp000066400000000000000000000112151407527632300206420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "iconimageprovider.h" #include #include #include #include #include #include #include #include #include // As there is only one instance of the IconImageProvider // and icons are added into the database using static methods, // engine has to be accessed via static property QQmlApplicationEngine *IconImageProvider::s_engine; IconImageProvider::IconImageProvider(QQmlApplicationEngine *engine) : QQuickImageProvider(QQmlImageProviderBase::Image) { s_engine = engine; } QString IconImageProvider::providerId() { return QStringLiteral("angelfish-favicon"); } QString IconImageProvider::storeImage(const QString &iconSource) { const QLatin1String prefix_favicon = QLatin1String("image://favicon/"); if (!iconSource.startsWith(prefix_favicon)) { // don't know what to do with it, return as it is qWarning() << Q_FUNC_INFO << "Don't know how to store image" << iconSource; return iconSource; } // new uri for image QString url = QStringLiteral("image://%1/%2").arg(providerId(), iconSource.mid(prefix_favicon.size())); // check if we have that image already QSqlQuery query_check; query_check.prepare(QStringLiteral("SELECT 1 FROM icons WHERE url = :url LIMIT 1")); query_check.bindValue(QStringLiteral(":url"), url); if (!query_check.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query_check.lastQuery(); qWarning() << query_check.lastError(); return iconSource; // as something is wrong } if (query_check.next()) { // there is corresponding record in the database already // no need to store it again return url; } query_check.finish(); // Store new icon QQuickImageProvider *provider = dynamic_cast(s_engine->imageProvider(QStringLiteral("favicon"))); if (!provider) { qWarning() << Q_FUNC_INFO << "Failed to load image provider" << url; return iconSource; // as something is wrong } QByteArray data; QBuffer buffer(&data); buffer.open(QIODevice::WriteOnly); const QSize szRequested; const QString providerIconName = iconSource.mid(prefix_favicon.size()); switch (provider->imageType()) { case QQmlImageProviderBase::Image: { const QImage image = provider->requestImage(providerIconName, nullptr, szRequested); if (!image.save(&buffer, "PNG")) { qWarning() << Q_FUNC_INFO << "Failed to save image" << url; return iconSource; // as something is wrong } break; } case QQmlImageProviderBase::Pixmap: { const QPixmap image = provider->requestPixmap(providerIconName, nullptr, szRequested); if (!image.save(&buffer, "PNG")) { qWarning() << Q_FUNC_INFO << "Failed to save pixmap" << url; return iconSource; // as something is wrong } break; } default: qWarning() << Q_FUNC_INFO << "Unsupported image provider" << provider->imageType(); return iconSource; // as something is wrong } QSqlQuery query_write; query_write.prepare(QStringLiteral("INSERT INTO icons(url, icon) VALUES (:url, :icon)")); query_write.bindValue(QStringLiteral(":url"), url); query_write.bindValue(QStringLiteral(":icon"), data); if (!query_write.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query_write.lastQuery(); qWarning() << query_write.lastError(); return iconSource; // as something is wrong } return url; } QImage IconImageProvider::requestImage(const QString &id, QSize *size, const QSize & /*requestedSize*/) { QSqlQuery query; query.prepare(QStringLiteral("SELECT icon FROM icons WHERE url LIKE :url LIMIT 1")); query.bindValue(QStringLiteral(":url"), QStringLiteral("image://%1/%2%").arg(providerId(), id)); if (!query.exec()) { qWarning() << Q_FUNC_INFO << "Failed to execute SQL statement"; qWarning() << query.lastQuery(); qWarning() << query.lastError(); return {}; } if (query.next()) { const QImage image = QImage::fromData(query.value(0).toByteArray()); if (size) { size->setHeight(image.height()); size->setWidth(image.width()); } return image; } qWarning() << "Failed to find icon for" << id; return {}; } angelfish-v21.07/lib/iconimageprovider.h000066400000000000000000000014651407527632300203150ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef ICONIMAGEPROVIDER_H #define ICONIMAGEPROVIDER_H #include class QQmlApplicationEngine; class IconImageProvider : public QQuickImageProvider { public: IconImageProvider(QQmlApplicationEngine *engine); virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; // store image into the database if it is missing. Return new // image:// uri that should be used to fetch the icon static QString storeImage(const QString &iconSource); static QString providerId(); private: static QQmlApplicationEngine *s_engine; }; #endif // ICONIMAGEPROVIDER_H angelfish-v21.07/lib/qquickwebenginedownloaditem.cpp000066400000000000000000000024511407527632300227260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "qquickwebenginedownloaditem.h" #include #include #include void QQuickWebEngineDownloadItem::accept() { QMetaObject::invokeMethod(this, "accept"); } void QQuickWebEngineDownloadItem::cancel() { QMetaObject::invokeMethod(this, "cancel"); } void QQuickWebEngineDownloadItem::pause() { QMetaObject::invokeMethod(this, "pause"); } void QQuickWebEngineDownloadItem::resume() { QMetaObject::invokeMethod(this, "resume"); } QString QQuickWebEngineDownloadItem::downloadDirectory() const { return property("downloadDirectory").value(); } QString QQuickWebEngineDownloadItem::downloadFileName() const { return property("downloadFileName").value(); } QUrl QQuickWebEngineDownloadItem::url() const { return property("url").value(); } QString QQuickWebEngineDownloadItem::mimeType() const { return property("mimeType").value(); } QQuickWebEngineDownloadItem::State QQuickWebEngineDownloadItem::state() const { return static_cast(property("state").value()); } QString QQuickWebEngineDownloadItem::interruptReasonString() const { return property("interruptReasonString").toString(); } angelfish-v21.07/lib/qquickwebenginedownloaditem.h000066400000000000000000000016741407527632300224010ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include // HACK, because QQuickWebEngineDownloadItem is not public API yet // Teach the compiler that QQuickWebEngineDownloadItem is a QObject subclass, // Because it can't know due to the forward declaration class QQuickWebEngineDownloadItem : public QObject { public: enum State { DownloadRequested, DownloadInProgress, DownloadCompleted, DownloadCancelled, DownloadInterrupted, }; QQuickWebEngineDownloadItem() = delete; // Created by the WebEngine, accessible in AngelfishWebProfile void accept(); void cancel(); void pause(); void resume(); QString downloadDirectory() const; QString downloadFileName() const; QUrl url() const; QString mimeType() const; State state() const; QString interruptReasonString() const; }; angelfish-v21.07/lib/resources.qrc000066400000000000000000000011321407527632300171460ustar00rootroot00000000000000 contents/ui/ErrorHandler.qml contents/ui/ListWebView.qml contents/ui/WebView.qml contents/ui/AuthSheet.qml contents/ui/DownloadQuestion.qml contents/ui/PermissionQuestion.qml contents/ui/JavaScriptDialogSheet.qml angelfish-v21.07/lib/settingshelper.cpp000066400000000000000000000010411407527632300201700ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include #include #include "settingshelper.h" inline bool parseQuickControlsMobile() { if (qEnvironmentVariableIsSet("QT_QUICK_CONTROLS_MOBILE")) { const QByteArray str = qgetenv("QT_QUICK_CONTROLS_MOBILE"); return str == "1" || str == "true"; } return false; } bool SettingsHelper::isMobile() { static bool mobile = parseQuickControlsMobile(); return mobile; } angelfish-v21.07/lib/settingshelper.h000066400000000000000000000003031407527632300176350ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #pragma once class SettingsHelper { public: static bool isMobile(); }; angelfish-v21.07/lib/sqlquerymodel.cpp000066400000000000000000000022231407527632300200410ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later // Based on https://wiki.qt.io/How_to_Use_a_QSqlQueryModel_in_QML #include "sqlquerymodel.h" #include #include #include #include SqlQueryModel::SqlQueryModel(QObject *parent) : QSqlQueryModel(parent) { } void SqlQueryModel::setQuery(const QSqlQuery &query) { QSqlQueryModel::setQuery(query); generateRoleNames(); } void SqlQueryModel::generateRoleNames() { m_roleNames.clear(); for (int i = 0; i < record().count(); i++) { m_roleNames.insert(Qt::UserRole + i + 1, record().fieldName(i).toUtf8()); } } QVariant SqlQueryModel::data(const QModelIndex &index, int role) const { if (role > Qt::UserRole) { const int columnIdx = role - Qt::UserRole - 1; const QModelIndex modelIndex = this->index(index.row(), columnIdx); return QSqlQueryModel::data(modelIndex, Qt::DisplayRole); } return {}; } QHash SqlQueryModel::roleNames() const { return m_roleNames; } angelfish-v21.07/lib/sqlquerymodel.h000066400000000000000000000017001407527632300175050ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later // Based on https://wiki.qt.io/How_to_Use_a_QSqlQueryModel_in_QML #ifndef SQLQUERYMODEL_H #define SQLQUERYMODEL_H #include /** * @class SqlQueryModel * @short Base class that can be used by models backed by SQL query */ class SqlQueryModel : public QSqlQueryModel { Q_OBJECT public: SqlQueryModel(QObject *parent = nullptr); // SQL query has to be executed when calling this // method. Note that the query result will determine // model role names. void setQuery(const QSqlQuery &query); QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; private: void generateRoleNames(); private: QHash m_roleNames; }; #endif // SQLQUERYMODEL_H angelfish-v21.07/lib/tabsmodel.cpp000066400000000000000000000232621407527632300171130ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #include "tabsmodel.h" #include #include #include #include #include #include #include #include #include "angelfishsettings.h" #include "browsermanager.h" TabsModel::TabsModel(QObject *parent) : QAbstractListModel(parent) { connect(this, &TabsModel::currentTabChanged, [this] { qDebug() << "Current tab changed to" << m_currentTab; }); // The fallback tab must not be saved, it would overwrite our actual data. m_tabsReadOnly = true; // Make sure model always contains at least one tab createEmptyTab(); // Only load tabs after private mode is known connect(this, &TabsModel::privateModeChanged, [this] { loadInitialTabs(); }); } QHash TabsModel::roleNames() const { return { {RoleNames::UrlRole, QByteArrayLiteral("pageurl")}, {RoleNames::IsMobileRole, QByteArrayLiteral("isMobile")}, }; } QVariant TabsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= m_tabs.count()) { return {}; } switch (role) { case RoleNames::UrlRole: return m_tabs.at(index.row()).url(); case RoleNames::IsMobileRole: return m_tabs.at(index.row()).isMobile(); } return {}; } int TabsModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_tabs.count(); } /** * @brief TabsModel::tab returns the tab at the given index * @param index * @return tab at the index */ TabState TabsModel::tab(int index) { if (index < 0 && index >= m_tabs.count()) return {}; // index out of bounds return m_tabs.at(index); } /** * @brief TabsModel::loadInitialTabs sets up the tabs that should already be open when starting the browser * This includes the configured homepage, an url passed on the command line (usually by another app) and tabs * which were still open when the browser was last closed. * * @warning It is impossible to save any new tabs until this function was called. */ void TabsModel::loadInitialTabs() { if (m_initialTabsLoaded) { return; } if (!m_privateMode) { loadTabs(); } m_tabsReadOnly = false; if (!m_privateMode) { if (BrowserManager::instance()->initialUrl().isEmpty()) { if (m_tabs.first().url() == QStringLiteral("about:blank")) setUrl(0, AngelfishSettings::self()->homepage()); } else { if (m_tabs.first().url() == QStringLiteral("about:blank")) setUrl(0, BrowserManager::instance()->initialUrl()); else newTab(BrowserManager::instance()->initialUrl()); } } m_initialTabsLoaded = true; } /** * @brief TabsModel::currentTab returns the index of the tab that is currently visible to the user * @return index */ int TabsModel::currentTab() const { return m_currentTab; } /** * @brief TabsModel::setCurrentTab sets the tab that is currently visible to the user * @param index */ void TabsModel::setCurrentTab(int index) { if (index >= m_tabs.count()) return; m_currentTab = index; emit currentTabChanged(); saveTabs(); } QVector TabsModel::tabs() const { return m_tabs; } /** * @brief TabsModel::loadTabs restores tabs saved in tabs.json * @return whether any tabs were restored */ bool TabsModel::loadTabs() { if (!m_privateMode) { beginResetModel(); const QString input = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/angelfish/tabs.json"); QFile inputFile(input); if (!inputFile.exists()) { return false; } if (!inputFile.open(QIODevice::ReadOnly)) { qDebug() << "Failed to load tabs from disk"; } const auto tabsStorage = QJsonDocument::fromJson(inputFile.readAll()).object(); m_tabs.clear(); const auto tabs = tabsStorage.value(QLatin1String("tabs")).toArray(); std::transform(tabs.begin(), tabs.end(), std::back_inserter(m_tabs), [](const QJsonValue &tab) { return TabState::fromJson(tab.toObject()); }); qDebug() << "loaded from file:" << m_tabs.count() << input; m_currentTab = tabsStorage.value(QLatin1String("currentTab")).toInt(); // Make sure model always contains at least one tab if (m_tabs.count() == 0) { createEmptyTab(); } endResetModel(); emit currentTabChanged(); return true; } return false; } /** * @brief TabsModel::saveTabs saves the current state of the model to disk * @return whether the tabs could be saved */ bool TabsModel::saveTabs() const { // only save if not in private mode if (!m_privateMode && !m_tabsReadOnly) { const QString outputDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/angelfish/"); QFile outputFile(outputDir + QStringLiteral("tabs.json")); if (!QDir(outputDir).mkpath(QStringLiteral("."))) { qDebug() << "Destdir doesn't exist and I can't create it: " << outputDir; return false; } if (!outputFile.open(QIODevice::WriteOnly)) { qDebug() << "Failed to write tabs to disk"; } QJsonArray tabsArray; std::transform(m_tabs.cbegin(), m_tabs.cend(), std::back_inserter(tabsArray), [](const TabState &tab) { return tab.toJson(); }); qDebug() << "Wrote to file" << outputFile.fileName() << "(" << tabsArray.count() << "urls" << ")"; const QJsonDocument document({ {QLatin1String("tabs"), tabsArray}, {QLatin1String("currentTab"), m_currentTab}, }); outputFile.write(document.toJson()); return true; } return false; } bool TabsModel::isMobileDefault() const { return m_isMobileDefault; } void TabsModel::setIsMobileDefault(bool def) { if (m_isMobileDefault != def) { m_isMobileDefault = def; emit isMobileDefaultChanged(); // used in initialization of the tab if (m_tabs.count() == 1) { setIsMobile(0, def); } } } bool TabsModel::privateMode() const { return m_privateMode; } void TabsModel::setPrivateMode(bool privateMode) { m_privateMode = privateMode; emit privateModeChanged(); } /** * @brief TabsModel::createEmptyTab convinience function for opening a tab containing "about:blank" */ void TabsModel::createEmptyTab() { newTab(QStringLiteral("about:blank")); }; /** * @brief TabsModel::newTab * @param url * @param isMobile */ void TabsModel::newTab(const QString &url) { beginInsertRows({}, m_tabs.count(), m_tabs.count()); m_tabs.append(TabState(url, m_isMobileDefault)); endInsertRows(); // Switch to last tab m_currentTab = m_tabs.count() - 1; emit currentTabChanged(); saveTabs(); } /** * @brief TabsModel::closeTab removes the tab at the index, handles moving the tabs after it and sets a new currentTab * @param index */ void TabsModel::closeTab(int index) { if (index < 0 && index >= m_tabs.count()) return; // index out of bounds if (m_tabs.count() <= 1) { // create new tab before removing the last one // to avoid linking all signals to null object createEmptyTab(); // now we have (tab_to_remove, "about:blank) // 0 will be the correct current tab index after tab_to_remove is gone m_currentTab = 0; // index to remove index = 0; } if (m_currentTab > index) { // decrease index if it's after the removed tab m_currentTab--; } if (m_currentTab == index) { // handle the removal of current tab // Just reset to first tab m_currentTab = 0; } beginRemoveRows({}, index, index); m_tabs.removeAt(index); endRemoveRows(); emit currentTabChanged(); saveTabs(); } void TabsModel::setIsMobile(int index, bool isMobile) { qDebug() << "Setting isMobile:" << index << isMobile << "tabs open" << m_tabs.count(); if (index < 0 && index >= m_tabs.count()) return; // index out of bounds m_tabs[index].setIsMobile(isMobile); const QModelIndex mindex = createIndex(index, index); emit dataChanged(mindex, mindex, {RoleNames::IsMobileRole}); saveTabs(); } void TabsModel::setUrl(int index, const QString &url) { qDebug() << "Setting URL:" << index << url << "tabs open" << m_tabs.count(); if (index < 0 && index >= m_tabs.count()) return; // index out of bounds m_tabs[index].setUrl(url); const QModelIndex mindex = createIndex(index, index); emit dataChanged(mindex, mindex, {RoleNames::UrlRole}); saveTabs(); } QString TabState::url() const { return m_url; } void TabState::setUrl(const QString &url) { m_url = url; } bool TabState::isMobile() const { return m_isMobile; } void TabState::setIsMobile(bool isMobile) { m_isMobile = isMobile; } TabState TabState::fromJson(const QJsonObject &obj) { TabState tab; tab.setUrl(obj.value(QStringLiteral("url")).toString()); tab.setIsMobile(obj.value(QStringLiteral("isMobile")).toBool()); return tab; } TabState::TabState(const QString &url, const bool isMobile) { setIsMobile(isMobile); setUrl(url); } bool TabState::operator==(const TabState &other) const { return (m_url == other.url() && m_isMobile == other.isMobile()); } QJsonObject TabState::toJson() const { return { {QStringLiteral("url"), m_url}, {QStringLiteral("isMobile"), m_isMobile}, }; } angelfish-v21.07/lib/tabsmodel.h000066400000000000000000000044261407527632300165610ustar00rootroot00000000000000/* * SPDX-FileCopyrightText: 2020 Jonah Brüchert * * SPDX-License-Identifier: LGPL-2.0-only */ #ifndef TABSMODEL_H #define TABSMODEL_H #include class QJsonObject; class TabState { public: static TabState fromJson(const QJsonObject &obj); QJsonObject toJson() const; TabState() = default; TabState(const QString &url, const bool isMobile); bool operator==(const TabState &other) const; bool isMobile() const; void setIsMobile(bool isMobile); QString url() const; void setUrl(const QString &url); private: QString m_url; bool m_isMobile = true; }; class TabsModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int currentTab READ currentTab WRITE setCurrentTab NOTIFY currentTabChanged) Q_PROPERTY(bool isMobileDefault READ isMobileDefault WRITE setIsMobileDefault NOTIFY isMobileDefaultChanged) Q_PROPERTY(bool privateMode READ privateMode WRITE setPrivateMode NOTIFY privateModeChanged REQUIRED) enum RoleNames { UrlRole = Qt::UserRole + 1, IsMobileRole }; public: explicit TabsModel(QObject *parent = nullptr); QHash roleNames() const override; QVariant data(const QModelIndex &index, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int currentTab() const; void setCurrentTab(int index); QVector tabs() const; Q_INVOKABLE TabState tab(int index); Q_INVOKABLE void loadInitialTabs(); Q_INVOKABLE void newTab(const QString &url); Q_INVOKABLE void createEmptyTab(); Q_INVOKABLE void closeTab(int index); Q_INVOKABLE void setUrl(int index, const QString &url); Q_INVOKABLE void setIsMobile(int index, bool isMobile); bool isMobileDefault() const; void setIsMobileDefault(bool def); bool privateMode() const; void setPrivateMode(bool privateMode); protected: bool loadTabs(); bool saveTabs() const; private: int m_currentTab = 0; QVector m_tabs{}; bool m_privateMode = false; bool m_tabsReadOnly = false; bool m_isMobileDefault = false; bool m_initialTabsLoaded = false; signals: void currentTabChanged(); void isMobileDefaultChanged(); void privateModeChanged(); }; #endif // TABSMODEL_H angelfish-v21.07/lib/urlobserver.cpp000066400000000000000000000017461407527632300175160ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "urlobserver.h" #include "browsermanager.h" UrlObserver::UrlObserver(QObject *parent) : QObject(parent) { connect(BrowserManager::instance(), &BrowserManager::databaseTableChanged, this, &UrlObserver::onDatabaseTableChanged); } QString UrlObserver::url() const { return m_url; } void UrlObserver::setUrl(const QString &url) { m_url = url; updateBookmarked(); emit urlChanged(url); } bool UrlObserver::bookmarked() const { return m_bookmarked; } void UrlObserver::onDatabaseTableChanged(const QString &table) { if (table != QStringView(u"bookmarks")) return; updateBookmarked(); } void UrlObserver::updateBookmarked() { if (const bool isBookmarked = BrowserManager::instance()->isBookmarked(m_url); isBookmarked != m_bookmarked) { m_bookmarked = isBookmarked; emit bookmarkedChanged(m_bookmarked); } } angelfish-v21.07/lib/urlobserver.h000066400000000000000000000014421407527632300171540ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef URLOBSERVER_H #define URLOBSERVER_H #include class UrlObserver : public QObject { Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged) Q_PROPERTY(bool bookmarked READ bookmarked NOTIFY bookmarkedChanged) Q_OBJECT public: explicit UrlObserver(QObject *parent = nullptr); QString url() const; void setUrl(const QString &url); bool bookmarked() const; signals: void urlChanged(const QString &url); void bookmarkedChanged(bool bookmarked); private: void onDatabaseTableChanged(const QString &table); void updateBookmarked(); private: QString m_url; bool m_bookmarked = false; }; #endif // URLOBSERVER_H angelfish-v21.07/lib/urlutils.cpp000066400000000000000000000027761407527632300170330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "urlutils.h" #include #include #include #include #include #include UrlUtils::UrlUtils(QObject *parent) : QObject(parent) { } QString UrlUtils::urlFromUserInput(const QString &input) { return QUrl::fromUserInput(input).toString(); } QString UrlUtils::urlScheme(const QString &url) { return QUrl::fromUserInput(url).scheme(); } QString UrlUtils::urlHostPort(const QString &url) { const QUrl u(url); static std::array common = { QStringView(u"www."), QStringView(u"m."), QStringView(u"mobile."), }; QString r = u.host(); for (const auto &i : common) { if (r.startsWith(i) && r.length() > i.length()) { r.remove(0, i.length()); break; // strip prefix only once } } const int p = u.port(-1); if (p > 0) r = QStringLiteral(u"%1:%2").arg(r).arg(p); return r; } QString UrlUtils::urlHost(const QString &url) { return QUrl::fromUserInput(url).host(); } QString UrlUtils::htmlFormattedUrl(const QString &url) { const QUrl parsedUrl = QUrl::fromUserInput(url); const QString path = parsedUrl.path(); return QStringView(uR"(%1%2)").arg(parsedUrl.host(), path == QStringView(u"/") ? QString() : path); } angelfish-v21.07/lib/urlutils.h000066400000000000000000000013761407527632300164730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef URLUTILS_H #define URLUTILS_H #include /** * @class UrlUtils * @short Utilities for URL manipulation and parsing. */ class UrlUtils : public QObject { Q_OBJECT public: UrlUtils(QObject *parent = nullptr); Q_INVOKABLE static QString urlFromUserInput(const QString &input); Q_INVOKABLE static QString urlScheme(const QString &url); Q_INVOKABLE static QString urlHostPort(const QString &url); Q_INVOKABLE static QString urlHost(const QString &url); Q_INVOKABLE static QString htmlFormattedUrl(const QString &url); }; #endif // URLUTILS_H angelfish-v21.07/lib/useragent.cpp000066400000000000000000000032421407527632300171320ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #include "useragent.h" #include #include UserAgent::UserAgent(QObject *parent) : QObject(parent) , m_defaultProfile(QQuickWebEngineProfile::defaultProfile()) , m_defaultUserAgent(m_defaultProfile->httpUserAgent()) , m_chromeVersion(extractValueFromAgent(u"Chrome")) , m_appleWebKitVersion(extractValueFromAgent(u"AppleWebKit")) , m_webEngineVersion(extractValueFromAgent(u"QtWebEngine")) , m_safariVersion(extractValueFromAgent(u"Safari")) , m_isMobile(true) { } QString UserAgent::userAgent() const { return QStringView( u"Mozilla/5.0 (%1) AppleWebKit/%2 (KHTML, like Gecko) QtWebEngine/%3 " u"Chrome/%4 %5 Safari/%6") .arg(m_isMobile ? u"Linux; Plasma Mobile, like Android 9.0" : u"X11; Linux x86_64", m_appleWebKitVersion, m_webEngineVersion, m_chromeVersion, m_isMobile ? u"Mobile" : u"Desktop", m_safariVersion); } bool UserAgent::isMobile() const { return m_isMobile; } void UserAgent::setIsMobile(bool value) { if (m_isMobile != value) { m_isMobile = value; emit isMobileChanged(); emit userAgentChanged(); } } QStringView UserAgent::extractValueFromAgent(const QStringView key) { const int index = m_defaultUserAgent.indexOf(key) + key.length() + 1; const int endIndex = m_defaultUserAgent.indexOf(u' ', index); return m_defaultUserAgent.midRef(index, endIndex - index); } angelfish-v21.07/lib/useragent.h000066400000000000000000000020601407527632300165740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later #ifndef USERAGENT_H #define USERAGENT_H #include class QQuickWebEngineProfile; class UserAgent : public QObject { Q_PROPERTY(QString userAgent READ userAgent NOTIFY userAgentChanged) Q_PROPERTY(bool isMobile READ isMobile WRITE setIsMobile NOTIFY isMobileChanged) Q_OBJECT public: explicit UserAgent(QObject *parent = nullptr); QString userAgent() const; bool isMobile() const; void setIsMobile(bool value); signals: void isMobileChanged(); void userAgentChanged(); private: QStringView extractValueFromAgent(const QStringView key); const QQuickWebEngineProfile *m_defaultProfile; const QString m_defaultUserAgent; const QStringView m_chromeVersion; const QStringView m_appleWebKitVersion; const QStringView m_webEngineVersion; const QStringView m_safariVersion; bool m_isMobile; }; #endif // USERAGENT_H angelfish-v21.07/org.kde.angelfish.desktop000077500000000000000000000065361407527632300205620ustar00rootroot00000000000000[Desktop Entry] Name=Angelfish Name[az]=Angelfish Name[ca]=Angelfish Name[ca@valencia]=Angelfish Name[cs]=Angelfish Name[da]=Angelfish Name[de]=Angelfish Name[el]=Angelfish Name[en_GB]=Angelfish Name[es]=Angelfish Name[et]=Angelfish Name[eu]=Angelfish Name[fi]=Angelfish Name[fr]=Angelfish Name[gl]=Angelfish Name[hu]=Angelfish Name[ia]=Angelfish Name[it]=Angelfish Name[ko]=Angelfish Name[lt]=Angelfish Name[nl]=Angelfish Name[nn]=Angelfish Name[pa]=ਐਂਗਲਫਿਸ਼ Name[pl]=Angelfish Name[pt]=Angelfish Name[pt_BR]=Angelfish Name[ru]=Angelfish Name[sk]=Angelfish Name[sl]=Angelfish Name[sv]=Angelfish Name[uk]=Angelfish Name[x-test]=xxAngelfishxx Name[zh_CN]=Angelfish Name[zh_TW]=Angelfish Comment=Mobile web browser Comment[az]=Mobil veb_bələdçi Comment[ca]=Navegador web mòbil Comment[ca@valencia]=Navegador web mòbil Comment[cs]=Mobilní webový prohlížeč Comment[da]=Mobil webbrowser Comment[de]=Webbrowser für Mobilgeräte Comment[el]=Περιηγητής ιστού για κινητά Comment[en_GB]=Mobile web browser Comment[es]=Navegador web para móviles Comment[et]=Mobiilne veebilehitseja Comment[eu]=Mugikorreko web arakatzailea Comment[fi]=Mobiiliverkkoselain Comment[fr]=Navigateur Web mobile Comment[gl]=Navegador web para móbiles Comment[hu]=Mobil webböngésző Comment[ia]=Navigator Web Mobile Comment[it]=Browser web per dispositivi mobili Comment[ko]=모바일 웹 브라우저 Comment[lt]=Mobilioji saityno naršyklė Comment[nl]=Webbrowser voor mobiel Comment[nn]=Mobil nettlesar Comment[pa]=ਮੋਬਾਈਲ ਵੈੱਬ ਬਰਾਊਜ਼ਰ Comment[pl]=Przenośna przeglądarka sieciowa Comment[pt]=Navegador Web móvel Comment[pt_BR]=Navegador Web móvel Comment[ru]=Веб-браузер для мобильных устройств Comment[sk]=Mobilný webový prehliadač Comment[sl]=Mobilni spletni brskalnik Comment[sv]=Mobilwebbläsare Comment[uk]=Браузер для мобільних пристроїв Comment[x-test]=xxMobile web browserxx Comment[zh_CN]=移动设备网页浏览器 Comment[zh_TW]=行動網頁瀏覽器 GenericName=Web Browser GenericName[az]=Veb Bələdçi GenericName[ca]=Navegador web GenericName[ca@valencia]=Navegador web GenericName[cs]=Webový prohlížeč GenericName[da]=Webbrowser GenericName[de]=Webbrowser GenericName[el]=Περιηγητής ιστού GenericName[en_GB]=Web Browser GenericName[es]=Navegador web GenericName[et]=Veebilehitseja GenericName[eu]=Web arakatzailea GenericName[fi]=Verkkoselain GenericName[fr]=Navigateur Web GenericName[gl]=Navegador web GenericName[hu]=Webböngésző GenericName[ia]=Navigator Web GenericName[it]=Browser web GenericName[ko]=웹 브라우저 GenericName[lt]=Saityno naršyklė GenericName[nl]=Webbrowser GenericName[nn]=Nettlesar GenericName[pa]=ਵੈੱਬ ਬਰਾਊਜ਼ਰ GenericName[pl]=Przeglądarka sieciowa GenericName[pt]=Navegador Web GenericName[pt_BR]=Navegador Web GenericName[ru]=Веб-браузер GenericName[sk]=Webový prehliadač GenericName[sl]=Spletni brskalnik GenericName[sv]=Webbläsare GenericName[uk]=Переглядач інтернету GenericName[x-test]=xxWeb Browserxx GenericName[zh_CN]=网页浏览器 GenericName[zh_TW]=網頁瀏覽器 Icon=org.kde.angelfish Exec=angelfish %u Type=Application X-DocPath=angelfish/index.html Categories=Qt;KDE;Network;WebBrowser; Terminal=false MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https; angelfish-v21.07/org.kde.angelfish.json000066400000000000000000000055601407527632300200530ustar00rootroot00000000000000{ "id": "org.kde.angelfish", "runtime": "org.kde.Platform", "runtime-version": "5.15", "sdk": "org.kde.Sdk", "base": "io.qt.qtwebengine.BaseApp", "base-version": "5.15", "command": "angelfish", "tags": ["nightly"], "desktop-file-name-suffix": " (Nightly)", "finish-args": [ "--share=ipc", "--share=network", "--socket=pulseaudio", "--socket=x11", "--socket=wayland", "--device=dri", "--filesystem=xdg-data", "--filesystem=xdg-download", "--talk-name=org.freedesktop.Notifications", "--own-name=org.kde.angelfish" ], "separate-locales": false, "add-extensions": { "org.freedesktop.Platform.ffmpeg-full": { "directory": "lib/ffmpeg", "add-ld-path": ".", "version": "20.08" } }, "cleanup-commands": [ "mkdir -p ${FLATPAK_DEST}/lib/ffmpeg" ], "sdk-extensions": [ "org.freedesktop.Sdk.Extension.rust-stable" ], "build-options": { "append-path": "/usr/lib/sdk/rust-stable/bin", "env": { "RUST_BACKTRACE": "1", "CARGO_NET_OFFLINE": "true", "RUSTFLAGS": "--remap-path-prefix =../" } }, "modules": [ { "name": "corrosion", "buildsystem": "cmake-ninja", "config-opts": [ "-DCORROSION_BUILD_TESTS=OFF" ], "build-options": { "env": { "CARGO_HOME": "/run/build/corrosion/cargo" } }, "cleanup": [ "/app" ], "sources": [ { "type": "git", "url": "https://github.com/AndrewGaspar/corrosion", "commit": "b7a78deb2632b502717c7b636a925677a99e06a1" }, "flatpak/corrosion-generated-sources.json" ] }, { "name": "kf5feedback", "buildsystem": "cmake-ninja", "sources": [ { "type": "git", "url": "https://invent.kde.org/jbbgameich/qtfeedback/", "branch": "kf5" } ] }, { "name": "angelfish", "buildsystem": "cmake-ninja", "config-opts": ["-DBUILD_TESTING=OFF", "-DCMAKE_BUILD_TYPE=Release", "-DQt5Feedback_DIR=/app/lib/cmake/Qt5Feedback"], "builddir": true, "build-options": { "env": { "CARGO_HOME": "/run/build/angelfish/cargo" } }, "sources": [ { "type": "dir", "path": ".", "skip": [".git"] }, "generated-sources.json" ] } ] } angelfish-v21.07/org.kde.angelfish.metainfo.xml000066400000000000000000000562231407527632300215050ustar00rootroot00000000000000 org.kde.angelfish Angelfish Web Browser Angelfish Veb Bələdçisi Navegador web Angelfish Navegador web Angelfish Webový prohlížeč Angelfish Angelfish-Webbrowser Angelfish περιηγητής ιστού Angelfish Web Browser Navegador web Angelfish Angelfish web-arakatzailea Angelfish-verkkoselain Navigateur Internet Angelfish Angelfish Webbrowser (navigator web) Browser web Angelfish Angelfish 웹 브라우저 Webbrowser Angelfish Angelfish nettlesar ਐਂਗਲਫਿਸ਼ ਵੈੱਬ ਬਰਾਊਜ਼ਰ Przeglądarka sieciowa Angelfish Navegador Web Angelfish Navegador Web Angelfish Webový prehliadač Angelfish Angelfish spletni brskalnik Angelfish webbläsare Веббраузер Angelfish xxAngelfish Web Browserxx Angelfish 网页浏览器 Webbrowser for mobile devices Mobil qurğular üçün Veb_Bələdçi Navegador web per a dispositius mòbils Navegador web per a dispositius mòbils Webový prohlížeč pro mobilní zařízení Webbrowser til mobile enheder Webbrowser für mobile Geräte Πειηγητής ιστού για κινητά Web browser for mobile devices Navegador web para dispositivos móviles Mobiilseadmete veebilehitseja Gailu mugikorretarako web-arakatzailea Verkkoselain mobiililaitteille Navigateur web pour les appareils mobiles Navegador web para dispositivos móbiles Webböngésző mobileszközökre Webbrowser (Navigator de web) per dispositivos mobile Webbrowser untuk perangkat ponsel Browser web per dispositivi mobili 모바일 장치용 웹 브라우저 Saityno naršyklė mobiliesiems įrenginiams Webbrowser voor mobiele apparaten Nettlesar for mobile einingar ਮੋਬਾਈਲ ਡਿਵਾਈਸਾਂ ਲਈ ਵੈੱਬ-ਬਰਾਊਜ਼ਰ Przeglądarka sieciowa przystosowana do urządzeń przenośnych Navegador Web para dispositivos móveis Navegador Web para dispositivos móveis Веб-браузер для мобильных устройств Webový prehliadač pre mobilné zariadenia Spletni brskalnik za mobilne naprave Webbläsare för mobila apparater Браузер для мобільних пристроїв xxWebbrowser for mobile devicesxx 移动设备的网络浏览器 適用於行動裝置的網頁瀏覽器 CC0-1.0 GPL-2.0-or-later KDE Community KDE İcması La comunitat KDE La comunitat KDE Komunita KDE KDE-fællesskabet KDE-Gemeinschaft Κοινότητα του KDE KDE Community Comunidad KDE KDE kogukond KDE komunitatea KDE-yhteisö La communauté KDE Comunidade KDE A KDE Közösség Communitate de KDE Komunitas KDE Comunità KDE KDE 커뮤니티 KDE bendruomenė KDE-gemeenschap KDE-fellesskapet ਕੇਡੀਈ ਕਮਿਊਨਟੀ Społeczność KDE Comunidade do KDE KDE Community Сообщество KDE KDE komunita Skupnost KDE KDE-gemenskapen Спільнота KDE xxKDE Communityxx KDE 社区 KDE 社群 https://invent.kde.org/plasma-mobile/angelfish/-/issues

Angelfish is a modern mobile webbrowser

Angelfish müasir mobil veb_bələdçidir

L'Angelfish és un navegador web modern per a mòbils

L'Angelfish és un navegador web modern per a mòbils

Angelfish je moderní mobilní webový prohlížeč

Angelfish er en moderne mobil-webbrowser

Angelfish ist ein moderner Webbrowser für mobile Geräte

Το Angelfish είναι ένας σύγχρονος περιηγητής ιστού για κινητά

Angelfish is a modern mobile web browser

Angelfish es un moderno navegador web móvil

Angelfish on tänapäevane mobiilne veebilehitseja

Angelfish web-arakatzaile mugikor moderno bat da

Angelfish on nykyaikainen mobiiliverkkoselain

Angelfish est un navigateur web mobile moderne

Angelfish é un navegador web móbil moderno

Az Angelfish egy modern mobilos webböngésző

Angelfish es un moderne Webbrowser (navigator web) de mobile

Angelfish adalah sebuah webbrowser ponsel yang modern

Angelfish è un browser web moderno per dispositivi mobili

Angelfish는 현대적인 모바일 웹 브라우저입니다

Angelfish yra šiuolaikinė mobilioji saityno naršyklė

Angelfish is een moderne mobiele webbrowser

Angelfish er ein moderne nettlesar for mobiltelefonar.

ਐਂਗਲਫਿਸ਼ ਨਵੇਂ ਜ਼ਮਾਨੇ ਦਾ ਮੋਬਾਈਲ ਵੈੱਬ-ਬਰਾਊਜ਼ਰ ਹੈ

Angelfish to nowoczesna, przenośna przeglądarka sieciowa

O Angelfish é um navegador Web móvel moderno

O Angelfish é um navegador Web móvel modem

Angelfish — это современный веб-браузер для мобильных устройств

Angelfish je moderný mobilný webový prehliadač

Angelfish je sodobni mobilni spletni brskalnik

Angelfish är en modern mobilwebbläsare

Angelfish — сучасний браузер для мобільних пристроїв

xxAngelfish is a modern mobile webbrowserxx

Angelfish 是一个现代化移动网络浏览器

Angelfish 是款現代化的行動網頁瀏覽器

It supports typical browser features, such as

O, veb bələdçilərə xas aşağıda göstərilən bir çox xüsusiyyətləri dəstəkləyir

Permet les funcionalitats típiques de navegació, com

Permet les funcionalitats típiques de navegació, com

Podporuje typické vlastnosti prohlížeče jako

Den understøtter almindelige browserfunktioner såsom

Es unterstützt die typischen Funktionen von Browsern wie

Υποστηρίζει τις τυπικές λειτουργίες ενός περιηγητή, όπως

It supports typical browser features, such as

Permite las funciones típicas de un navegador, como

See toetab tüüpilisi veebilehitseja võimalusi, nagu

Arakatzaileen ohiko ezaugarriak ditu, besteak beste

Tukee tavallisia selainominaisuuksia kuten

Il prend en charge les fonctionnalités classiques des navigateurs :

Goza das funcionalidades habituais dos navegadores, como

Támogatja a megszokott szolgáltatásokat, úgy mint

Il supporta typic characteristicas de navigator, tal como

Ini mendukung fitur-fitur browser biasanya, sepertihalnya

Supporta le funzionalità tipiche del browser, quali

다음과 같은 웹 브라우저 기능을 제공합니다

Ji palaiko tipines naršyklės ypatybes, tokias kaip

Het ondersteunt typische browserfuncties, zoals

Han støttar vanlege nettlesarfunksjonar:

ਇਹ ਸਧਾਰਨ ਬਰਾਊਜ਼ਰ ਫੀਚਰਾਂ ਲਈ ਸਹਾਇਕ ਹੈ ਜਿਵੇਂ ਕਿ

Obsługuje zwyczajne funkcje przeglądarki, takie jak

Suporta as funcionalidades típicas de navegação, como

Tem suporte a funcionalidades típicas de navegação, como

Он поддерживает типичные функции веб-браузеров, такие как

Podporuje typické funkcie prehliadača, ako napr.

Podpira tipične možnosti brskalnikov, kot so

Den stöder typiska webbläsarfunktioner, såsom

Передбачено типові можливості браузера, зокрема

xxIt supports typical browser features, such asxx

它支持典型的浏览器功能,例如:

此瀏覽器支援一般瀏覽器會有的功能,例如

  • bookmarks
  • əlfəcinlər
  • adreces d'interès
  • adreces d'interés
  • záložky
  • bogmærker
  • Lesezeichen
  • σελιδοδείκτες
  • bookmarks
  • marcadores
  • järjehoidjad
  • laster-markak
  • kirjanmerkit
  • signets
  • marcadores
  • könyvjelzők
  • Marcatores de libro
  • markah
  • segnalibri
  • 책갈피
  • žymeles
  • bladwijzers
  • bokmerke
  • ਬੁੱਕਮਾਰਕ
  • zakładki
  • favoritos
  • favoritos
  • закладки
  • zalozky
  • zaznamki
  • bokmärken
  • закладки
  • xxbookmarksxx
  • 书签
  • 書籤
  • history
  • tarixçə
  • historial
  • historial
  • historie
  • historik
  • Verlauf
  • ιστορικό
  • history
  • historial
  • ajalugu
  • historiala
  • historia
  • historique
  • historial
  • előzmények
  • Chronologia
  • histori
  • cronologia
  • 과거 기록
  • istoriją
  • geschiedenis
  • besøkslogg
  • ਅਤੀਤ
  • historia
  • histórico
  • histórico
  • журнал
  • História
  • zgodovino brskanja
  • historik
  • журнал
  • xxhistoryxx
  • 历史记录
  • 歷史記錄
  • tabs
  • vərəqlər
  • pestanyes
  • pestanyes
  • karty
  • faneblade
  • Unterfenster
  • καρτέλες
  • tabs
  • pestañas
  • kaardid
  • fitxak
  • välilehdet
  • onglets
  • separadores
  • lapok
  • schedas
  • tab
  • schede
  • korteles
  • tabs
  • faner
  • ਟੈਬਾਂ
  • karty
  • páginas
  • abas
  • вкладки
  • karty
  • zavihke
  • flikar
  • вкладки
  • xxtabsxx
  • 标签页
  • 分頁
Homepage Ev səhifəsi Pàgina inicial Pàgina inicial Domovská stránka Startside Internetseite Προσωπική σελίδα Homepage Página de inicio Kodulehekülg Orri nagusia Kotisivu Page d'accueil Páxina inicial Kezdőlap Pagina Domo o Principal Laman Pagina iniziale 홈페이지 Pagrindinis puslapis Homepagina Heimeside ਮੁੱਖ ਸਫ਼ਾ Strona domowa Página Web Site Домашняя страница Domovská stránka Domača stran Hemsida Домашня сторінка xxHomepagexx 主页 首頁 https://cdn.kde.org/screenshots/plasma-angelfish/homepage.png Menu Actions Menyu fəaliyyətləri Accions de menú Accions de menú Činnosti nabídky Menuhandlinger Menüaktionen Ενέργειες μενού Menu Actions Acciones del menú Menüütoimingud Menu-ekintzak Valikkotoimenpiteet Actions de menu Accións de menú Menüműveletek Actiones de menu Aksi Menu Menu Azioni 메뉴 동작 Meniu veiksmai Menuopties Menyhandlingar ਮੇਨੂ ਕਾਰਵਾਈਆਂ Działania menu Acções do Menu Menu de ações Действия меню Akcie ponuky Dejavnosti menija Menyalternativ Пункти меню xxMenu Actionsxx 菜单行为 選單動作 https://cdn.kde.org/screenshots/plasma-angelfish/actions.png Tabs View Vərəq görünüşü Vista en pestanyes Vista en pestanyes Prohlížení karet Unterfensteransicht Προβολή καρτελών Tabs View Vista de pestañas Fitxa-ikuspegia Välilehtinäkymä Vue par onglets Vista de Schedas Vista a schede 탭 보기 Tabbladenweergave Widok kart Vista de Páginas Visualização das abas Pogled zavihkov Flikvy Перегляд з вкладками xxTabs Viewxx 标签视图 https://cdn.kde.org/screenshots/plasma-angelfish/tabs.png https://invent.kde.org/plasma-mobile/angelfish https://invent.kde.org/plasma-mobile/angelfish/-/issues angelfish org.kde.angelfish.desktop
  • Improve the startup time of the ad blocker
  • Fix various bugs and safety issues in the web app manager
  • Improve flatpak support
https://www.plasma-mobile.org/2021/07/20/plasma-mobile-gear-21-07/
  • Add a settings page for managing web apps
https://www.plasma-mobile.org/2021/06/10/plasma-mobile-update-june/
angelfish-v21.07/org.kde.angelfish.svg000066400000000000000000000133341407527632300176770ustar00rootroot00000000000000 angelfish-v21.07/src/000077500000000000000000000000001407527632300144515ustar00rootroot00000000000000angelfish-v21.07/src/CMakeLists.txt000066400000000000000000000031201407527632300172050ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2020 Jonah Brüchert # SPDX-FileCopyrightText: 2020 Rinigus # # SPDX-License-Identifier: LGPL-2.0-or-later set(angelfish_SRCS main.cpp adblockfilterlistsmodel.cpp adblockfilterlistsmanager.cpp adblockurlinterceptor.cpp downloadsmodel.cpp webappcreator.cpp webappmanager.cpp webappmanagermodel.cpp angelfish.qrc ) add_executable(angelfish ${angelfish_SRCS}) if (Corrosion_FOUND) add_custom_target(adblock SOURCES rs/adblock/Cargo.toml rs/adblock/build.rs rs/adblock/src/lib.rs rs/adblock/src/adblock.rs rs/adblock/src/domainresolver.rs rs/adblock/src/logging.rs) corrosion_import_crate(MANIFEST_PATH ${CMAKE_CURRENT_SOURCE_DIR}/rs/adblock/Cargo.toml) target_link_libraries(angelfish PRIVATE angelfish-adblock) target_compile_definitions(angelfish PRIVATE -DBUILD_ADBLOCK) target_include_directories(angelfish PRIVATE ${CMAKE_BINARY_DIR}/cargo/build/${Rust_CARGO_TARGET}/cxxbridge/angelfish-adblock/src/) endif() target_include_directories(angelfish PRIVATE ${CMAKE_BINARY_DIR}) target_compile_definitions(angelfish PRIVATE -DQT_NO_CAST_FROM_ASCII) target_link_libraries(angelfish PRIVATE Qt5::Core Qt5::Qml Qt5::Quick Qt5::Sql Qt5::Svg Qt5::WebEngine KF5::I18n KF5::ConfigCore KF5::ConfigGui KF5::DBusAddons KF5::WindowSystem AngelfishCore ) install(TARGETS angelfish ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES angelfish.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) angelfish-v21.07/src/adblockfilterlistsmanager.cpp000066400000000000000000000067221407527632300224030ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "adblockfilterlistsmanager.h" #include #include #include #include #include "angelfishsettings.h" void copyStream(QIODevice &input, QIODevice &output) { constexpr auto BUFFER_SIZE = 1024; QByteArray buffer; buffer.reserve(BUFFER_SIZE); while (true) { int64_t read = input.read(buffer.data(), BUFFER_SIZE); if (read > 0) { output.write(buffer.data(), read); } else { break; } } } AdblockFilterListsManager::AdblockFilterListsManager(QObject *parent) : QObject(parent) , m_filterLists(loadFromConfig()) { connect(&m_networkManager, &QNetworkAccessManager::finished, this, &AdblockFilterListsManager::handleListFetched); m_networkManager.setRedirectPolicy(QNetworkRequest::SameOriginRedirectPolicy); } void AdblockFilterListsManager::refreshLists() { for (const auto &list : std::as_const(m_filterLists)) { m_runningRequests++; m_networkManager.get(QNetworkRequest(list.url)); } } QString AdblockFilterListsManager::filterListPath() { static const QString path = []() { auto path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/filterlists/"); QDir(path).mkpath(QStringLiteral(".")); return path; }(); return path; } void AdblockFilterListsManager::handleListFetched(QNetworkReply *reply) { Q_ASSERT(reply); m_runningRequests--; if (m_runningRequests < 1) { Q_EMIT refreshFinished(); } QFile file(filterListPath() + reply->url().fileName()); if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open" << file.fileName() << "for writing." << "Filter list not updated"; return; } copyStream(*reply, file); } QVector AdblockFilterListsManager::loadFromConfig() { const auto &filterNames = AngelfishSettings::adblockFilterNames(); const auto &filterUrls = AngelfishSettings::adblockFilterUrls(); auto namesIt = filterNames.begin(); auto urlsIt = filterUrls.begin(); QVector out; // Otherwise list is corrupted, but we will still not crash in release mode Q_ASSERT(filterNames.size() == filterUrls.size()); while (namesIt != filterNames.end() && urlsIt != filterUrls.end()) { out.push_back(FilterList{*namesIt, *urlsIt}); namesIt++; urlsIt++; } return out; } void AdblockFilterListsManager::writeToConfig(const QVector &filters) { QStringList filterNames; QList filterUrls; for (const auto &filterList : filters) { filterNames.push_back(filterList.name); filterUrls.push_back(filterList.url); } AngelfishSettings::setAdblockFilterNames(filterNames); AngelfishSettings::setAdblockFilterUrls(filterUrls); } const QVector &AdblockFilterListsManager::filterLists() const { return m_filterLists; } void AdblockFilterListsManager::addFilterList(const QString &name, const QUrl &url) { m_filterLists.push_back(FilterList{name, url}); writeToConfig(m_filterLists); } void AdblockFilterListsManager::removeFilterList(const int index) { m_filterLists.removeAt(index); writeToConfig(m_filterLists); } angelfish-v21.07/src/adblockfilterlistsmanager.h000066400000000000000000000017611407527632300220460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include class AdblockFilterListsManager : public QObject { Q_OBJECT public: AdblockFilterListsManager(QObject *parent = nullptr); struct FilterList { QString name; QUrl url; }; /// filterListPath always returns an existing path to a filter list directory static QString filterListPath(); void refreshLists(); Q_SIGNAL void refreshFinished(); const QVector &filterLists() const; void addFilterList(const QString &name, const QUrl &url); void removeFilterList(const int index); private: Q_SLOT void handleListFetched(QNetworkReply *reply); static QVector loadFromConfig(); static void writeToConfig(const QVector &filters); QVector m_filterLists; QNetworkAccessManager m_networkManager; int m_runningRequests = 0; }; angelfish-v21.07/src/adblockfilterlistsmodel.cpp000066400000000000000000000033661407527632300220720ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "adblockfilterlistsmodel.h" #include "adblockfilterlistsmanager.h" #include "adblockurlinterceptor.h" #include "angelfishsettings.h" AdblockFilterListsModel::AdblockFilterListsModel(QObject *parent) : QAbstractListModel(parent) { connect(&m_manager, &AdblockFilterListsManager::refreshFinished, this, &AdblockFilterListsModel::refreshFinished); connect(&m_manager, &AdblockFilterListsManager::refreshFinished, this, &AdblockFilterListsModel::resetAdblock); } QVariant AdblockFilterListsModel::data(const QModelIndex &index, int role) const { switch (role) { case Qt::DisplayRole: return m_manager.filterLists().at(index.row()).name; case Role::Url: return m_manager.filterLists().at(index.row()).url; } return {}; } QHash AdblockFilterListsModel::roleNames() const { return {{Qt::DisplayRole, "displayName"}, {Role::Url, "url"}}; } int AdblockFilterListsModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : m_manager.filterLists().size(); } void AdblockFilterListsModel::addFilterList(const QString &name, const QUrl &url) { const auto currentSize = m_manager.filterLists().size(); beginInsertRows({}, currentSize, currentSize); m_manager.addFilterList(name, url); endInsertRows(); } void AdblockFilterListsModel::removeFilterList(const int index) { beginRemoveRows({}, index, index); m_manager.removeFilterList(index); endRemoveRows(); } void AdblockFilterListsModel::refreshLists() { m_manager.refreshLists(); } void AdblockFilterListsModel::resetAdblock() { AdblockUrlInterceptor::instance().resetAdblock(); } angelfish-v21.07/src/adblockfilterlistsmodel.h000066400000000000000000000016261407527632300215340ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include "adblockfilterlistsmanager.h" class AdblockFilterListsModel : public QAbstractListModel { Q_OBJECT enum Role { Url = Qt::UserRole + 1, }; public: explicit AdblockFilterListsModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; int rowCount(const QModelIndex &parent) const override; Q_SLOT void addFilterList(const QString &name, const QUrl &url); Q_SLOT void removeFilterList(const int index); Q_SLOT void refreshLists(); Q_SLOT void resetAdblock(); Q_SIGNAL void refreshFinished(); private: AdblockFilterListsManager m_manager; }; angelfish-v21.07/src/adblockurlinterceptor.cpp000066400000000000000000000115341407527632300215620ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "adblockurlinterceptor.h" #include #include #include #include #include #include #include "adblockfilterlistsmanager.h" #include "angelfishsettings.h" Q_LOGGING_CATEGORY(AdblockCategory, "org.kde.angelfish.adblock", QtWarningMsg); AdblockUrlInterceptor::AdblockUrlInterceptor(QObject *parent) : QWebEngineUrlRequestInterceptor(parent) #ifdef BUILD_ADBLOCK // parsing the block lists takes some time, try to do it asynchronously // if it is not ready when it's needed, reading the future will block , m_adblockInitFuture(std::async(std::launch::async, [this] { return createOrRestoreAdblock(); })) , m_adblock(std::nullopt) #endif , m_enabled(AngelfishSettings::adblockEnabled()) { #ifdef BUILD_ADBLOCK connect(this, &AdblockUrlInterceptor::adblockInitialized, this, [this] { if (m_adblockInitFuture.valid()) { qDebug() << "Adblock ready"; m_adblock = m_adblockInitFuture.get(); } }); #endif } #ifdef BUILD_ADBLOCK rust::Box AdblockUrlInterceptor::createOrRestoreAdblock() { rust::Box adb = [] { auto cacheLocation = adblockCacheLocation(); if (QFile::exists(cacheLocation)) { return loadAdblock(cacheLocation.toStdString()); } return newAdblock(AdblockFilterListsManager::filterListPath().toStdString()); }(); Q_EMIT adblockInitialized(); return adb; } #endif QString AdblockUrlInterceptor::adblockCacheLocation() { return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) % u"/adblockCache"; } bool AdblockUrlInterceptor::enabled() const { return m_enabled; } void AdblockUrlInterceptor::setEnabled(bool enabled) { m_enabled = enabled; AngelfishSettings::setAdblockEnabled(enabled); } AdblockUrlInterceptor &AdblockUrlInterceptor::instance() { static AdblockUrlInterceptor instance; return instance; } AdblockUrlInterceptor::~AdblockUrlInterceptor() { #ifdef BUILD_ADBLOCK if (m_adblock && (*m_adblock)->isValid() && (*m_adblock)->needsSave()) { (*m_adblock)->save(adblockCacheLocation().toStdString()); } #endif } bool AdblockUrlInterceptor::downloadNeeded() const { return QDir(AdblockFilterListsManager::filterListPath()).isEmpty(); } void AdblockUrlInterceptor::resetAdblock() { #ifdef BUILD_ADBLOCK if (m_adblock) { m_adblock = std::nullopt; } m_adblockInitFuture = std::async(std::launch::async, [this] { auto adb = newAdblock(AdblockFilterListsManager::filterListPath().toStdString()); Q_EMIT adblockInitialized(); return adb; }); #endif } inline std::string resourceTypeToString(const QWebEngineUrlRequestInfo::ResourceType type) { // Strings from https://docs.rs/crate/adblock/0.3.3/source/src/request.rs using Type = QWebEngineUrlRequestInfo::ResourceType; switch (type) { case Type::ResourceTypeMainFrame: return "main_frame"; case Type::ResourceTypeSubFrame: return "sub_frame"; case Type::ResourceTypeStylesheet: return "stylesheet"; case Type::ResourceTypeScript: return "script"; case Type::ResourceTypeFontResource: return "font"; case Type::ResourceTypeImage: return "image"; case Type::ResourceTypeSubResource: return "object_subrequest"; // TODO CHECK case Type::ResourceTypeObject: return "object"; case Type::ResourceTypeMedia: return "media"; case Type::ResourceTypeFavicon: return "image"; // almost case Type::ResourceTypeXhr: return "xhr"; case Type::ResourceTypePing: return "ping"; case Type::ResourceTypeCspReport: return "csp_report"; default: return "other"; } } void AdblockUrlInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { #ifdef BUILD_ADBLOCK if (!m_enabled) { return; } // Only wait for the adblock initialization if it isn't ready on first use if (!m_adblock) { qDebug() << "Adblock not yet initialized, blindly allowing request"; return; } const std::string url = info.requestUrl().toString().toStdString(); const std::string firstPartyUrl = info.firstPartyUrl().toString().toStdString(); const AdblockResult result = m_adblock.value()->shouldBlock(url, firstPartyUrl, resourceTypeToString(info.resourceType())); const auto &redirect = result.redirect; if (redirect.begin() != redirect.end()) { info.redirect(QUrl(QString::fromStdString({redirect.begin(), redirect.end()}))); } else { info.block(result.matched); } #else Q_UNUSED(info); #endif } void q_cdebug_adblock(const char *message) { qCDebug(AdblockCategory) << message; } angelfish-v21.07/src/adblockurlinterceptor.h000066400000000000000000000035101407527632300212220ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include class QWebEngineUrlRequestInfo; class QQuickWebEngineProfile; #ifdef BUILD_ADBLOCK #include #include #endif #include class AdblockUrlInterceptor : public QWebEngineUrlRequestInterceptor { Q_OBJECT Q_PROPERTY(bool downloadNeeded READ downloadNeeded NOTIFY downloadNeededChanged) Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool adblockSupported READ adblockSupported CONSTANT) public: static AdblockUrlInterceptor &instance(); ~AdblockUrlInterceptor(); void interceptRequest(QWebEngineUrlRequestInfo &info) override; /// returns true when no filterlists exist bool downloadNeeded() const; Q_SIGNAL void downloadNeededChanged(); /// Deletes the old adblock and creates a new one from the current filter lists. /// This needs to be called after lists were changed. void resetAdblock(); Q_SIGNAL void adblockInitialized(); Q_SIGNAL void enabledChanged(); constexpr static bool adblockSupported() { #ifdef BUILD_ADBLOCK return true; #endif return false; } bool enabled() const; void setEnabled(bool enabled); private: explicit AdblockUrlInterceptor(QObject *parent = nullptr); #ifdef BUILD_ADBLOCK /// If an adblock cache is found, loads it, otherwise creates a new adblock /// from the current filter lists. rust::Box createOrRestoreAdblock(); std::future> m_adblockInitFuture; std::optional> m_adblock; #endif static QString adblockCacheLocation(); bool m_enabled; }; extern "C" { void q_cdebug_adblock(const char *message); } angelfish-v21.07/src/angelfish.notifyrc000066400000000000000000000050511407527632300201710ustar00rootroot00000000000000[Global] IconName=org.kde.angelfish DesktopEntry=org.kde.angelfish Comment=Webbrowser Comment[az]=Veb Bələdçi Comment[ca]=Navegador web Comment[ca@valencia]=Navegador web Comment[cs]=Webový prohlížeč Comment[de]=Webbrowser Comment[el]=Περιηγητής ιστού Comment[en_GB]=Web browser Comment[es]=Navegador web Comment[eu]=Web-arakatzailea Comment[fi]=Verkkoselain Comment[fr]=Navigateur Internet Comment[hu]=Webböngésző Comment[ia]=Navigator Web Comment[it]=Browser web Comment[ko]=웹 브라우저 Comment[nl]=Webbrowser Comment[nn]=Nettlesar Comment[pa]=ਵੈਬ-ਬਰਾਊਜ਼ਰ Comment[pl]=Przeglądarka sieciowa Comment[pt]=Navegador Web Comment[pt_BR]=Navegador Web Comment[sk]=Webový prehliadač Comment[sl]=Spletni brskalnik Comment[sv]=webbläsare Comment[uk]=Браузер Comment[x-test]=xxWebbrowserxx Comment[zh_CN]=网页浏览器 Name=Angelfish Name[az]=Angelfish Name[ca]=Angelfish Name[ca@valencia]=Angelfish Name[cs]=Angelfish Name[da]=Angelfish Name[de]=Angelfish Name[el]=Angelfish Name[en_GB]=Angelfish Name[es]=Angelfish Name[et]=Angelfish Name[eu]=Angelfish Name[fi]=Angelfish Name[fr]=Angelfish Name[gl]=Angelfish Name[hu]=Angelfish Name[ia]=Angelfish Name[it]=Angelfish Name[ko]=Angelfish Name[lt]=Angelfish Name[nl]=Angelfish Name[nn]=Angelfish Name[pa]=ਐਂਗਲਫਿਸ਼ Name[pl]=Angelfish Name[pt]=Angelfish Name[pt_BR]=Angelfish Name[ru]=Angelfish Name[sk]=Angelfish Name[sl]=Angelfish Name[sv]=Angelfish Name[uk]=Angelfish Name[x-test]=xxAngelfishxx Name[zh_CN]=Angelfish Name[zh_TW]=Angelfish [Event/web-notification] Name=Website Notification Name[az]=Veb sayt bildirişləri Name[ca]=Notificació de lloc web Name[ca@valencia]=Notificació de lloc web Name[cs]=Oznámení webové stránky Name[de]=Webseiten-Benachrichtigung Name[el]=Ειδοποίηση ιστοτόπου Name[en_GB]=Website Notification Name[es]=Notificación de sitio web Name[eu]=Webguneko jakinarazpena Name[fi]=Sivustoilmoitukset Name[fr]=Notification de site Internet Name[hu]=Weboldal értesítések Name[ia]=Notification e sito web Name[it]=Notifica dal sito Name[ko]=웹사이트 알림 Name[nl]=Websitemelding Name[nn]=Nettstad-varsling Name[pa]=ਵੈੱਬਸਾਈਟ ਲਈ ਨੋਟੀਫਿਕੇਸ਼ਨ Name[pl]=Powiadomienia przeglądarki sieciowej Name[pt]=Notificação da Página Web Name[pt_BR]=Notificação do site Name[sk]=Oznámenia webových stránok Name[sl]=Obvestilo spletišča Name[sv]=Underrättelse om webbplats Name[uk]=Сповіщення від сайта Name[x-test]=xxWebsite Notificationxx Name[zh_CN]=网站通知 Action=Popup angelfish-v21.07/src/angelfish.qrc000066400000000000000000000031221407527632300171160ustar00rootroot00000000000000 contents/ui/FindInPageBar.qml regex-weburl/regex-weburl.js contents/ui/ShareSheet.qml contents/ui/AdblockFilterDownloadQuestion.qml contents/ui/SettingsAdblock.qml contents/ui/SettingsPage.qml contents/ui/SettingsNavigationBarPage.qml contents/ui/SettingsSearchEnginePage.qml contents/ui/Navigation.qml contents/ui/webbrowser.qml contents/ui/NavigationEntrySheet.qml contents/ui/NewTabQuestion.qml contents/ui/Tabs.qml contents/ui/History.qml contents/ui/HistorySheet.qml contents/ui/InputSheet.qml contents/ui/UrlDelegate.qml contents/ui/Bookmarks.qml contents/ui/Downloads.qml contents/ui/SettingsWebApps.qml angelfish-v21.07/src/contents/000077500000000000000000000000001407527632300163065ustar00rootroot00000000000000angelfish-v21.07/src/contents/ui/000077500000000000000000000000001407527632300167235ustar00rootroot00000000000000angelfish-v21.07/src/contents/ui/AdblockFilterDownloadQuestion.qml000066400000000000000000000015551407527632300253710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import org.kde.kirigami 2.4 as Kirigami import org.kde.angelfish 1.0 Kirigami.InlineMessage { id: question showCloseButton: true visible: AdblockUrlInterceptor.adblockSupported text: i18n("The ad blocker is missing its filter lists, do you want to download them now?") AdblockFilterListsModel { id: filterListsModel onRefreshFinished: question.visible = false } actions: [ Kirigami.Action { id: downloadAction iconName: "download" text: i18n("Download") onTriggered: { filterListsModel.refreshLists() downloadAction.enabled = false; downloadAction.text = i18n("Downloading...") } } ] } angelfish-v21.07/src/contents/ui/Bookmarks.qml000066400000000000000000000037421407527632300213740ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.8 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Bookmarks") Kirigami.ColumnView.fillWidth: false header: Item { anchors.horizontalCenter: parent.horizontalCenter height: Kirigami.Units.gridUnit * 3 width: list.width Kirigami.SearchField { id: search anchors.centerIn: parent width: parent.width - Kirigami.Units.gridUnit clip: true inputMethodHints: rootPage.privateMode ? Qt.ImhNoPredictiveText : Qt.ImhNone Kirigami.Theme.inherit: true onDisplayTextChanged: { if (displayText === "" || displayText.length > 2) { list.model.filter = displayText; timer.running = false; } else timer.running = true; } Keys.onEscapePressed: pageStack.pop() Timer { id: timer repeat: false interval: Math.max(1000, 3000 - search.displayText.length * 1000) onTriggered: list.model.filter = search.displayText } } } Component { id: delegateComponent UrlDelegate { highlightText: list.model.filter onClicked: { currentWebView.url = url; pageStack.pop(); } onRemoved: BrowserManager.removeBookmark(url); } } ListView { id: list anchors.fill: parent interactive: height < contentHeight clip: true model: BookmarksHistoryModel { bookmarks: true } delegate: Kirigami.DelegateRecycler { width: list.width sourceComponent: delegateComponent } } } angelfish-v21.07/src/contents/ui/Downloads.qml000066400000000000000000000073531407527632300214000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.0 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.4 as Controls import QtWebEngine 1.7 import org.kde.kirigami 2.14 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Downloads") Kirigami.ColumnView.fillWidth: false ListView { model: DownloadsModel { id: downloadsModel } Kirigami.PlaceholderMessage { anchors.centerIn: parent width: parent.width - (Kirigami.Units.largeSpacing * 4) visible: parent.count === 0 text: i18n("No running downloads") } delegate: Kirigami.SwipeListItem { onClicked: Qt.openUrlExternally(model.downloadedFilePath) actions: [ Kirigami.Action { text: i18n("Cancel") icon.name: model.download.state === WebEngineDownloadItem.DownloadInProgress ? "dialog-cancel" : "list-remove" onTriggered: downloadsModel.removeDownload(index) }, Kirigami.Action { visible: !model.download.isPaused && model.download.state === WebEngineDownloadItem.DownloadInProgress text: i18n("Pause") icon.name: "media-playback-pause" onTriggered: model.download.pause() }, Kirigami.Action { visible: model.download.isPaused && model.download.state === WebEngineDownloadItem.DownloadInProgress text: i18n("Continue") icon.name: "media-playback-start" onTriggered: model.download.resume(); } ] RowLayout { Kirigami.Icon { source: model.mimeTypeIcon height: Kirigami.Units.iconSizes.medium width: height } ColumnLayout { Layout.fillWidth: true Kirigami.Heading { Layout.fillWidth: true level: 3 elide: Qt.ElideRight text: model.fileName } Controls.Label { Layout.fillWidth: true elide: Qt.ElideRight text: model.url } Controls.ProgressBar { Layout.fillWidth: true visible: model.download.state === WebEngineDownloadItem.DownloadInProgress from: 0 value: model.download.receivedBytes to: model.download.totalBytes } Controls.Label { visible: model.download.state !== WebEngineDownloadItem.DownloadInProgress text: { switch (model.download.state) { case WebEngineDownloadItem.DownloadRequested: return i18nc("download state", "Starting…"); case WebEngineDownloadItem.DownloadCompleted: return i18n("Completed"); case WebEngineDownloadItem.DownloadCancelled: return i18n("Cancelled"); case WebEngineDownloadItem.DownloadInterrupted: return i18nc("download state", "Interrupted"); } } } } } } } } angelfish-v21.07/src/contents/ui/FindInPageBar.qml000066400000000000000000000070021407527632300220260ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Layouts 1.0 import QtQuick.Controls 2.0 as Controls import org.kde.kirigami 2.5 as Kirigami import org.kde.angelfish 1.0 Item { id: findInPage anchors { top: parent.bottom left: parent.left right: parent.right } height: Kirigami.Units.gridUnit * 3 property bool active: false property int buttonSize: Kirigami.Units.gridUnit * 2 Rectangle { anchors.fill: parent; color: Kirigami.Theme.backgroundColor; } RowLayout { id: layout anchors.fill: parent anchors.leftMargin: Kirigami.Units.gridUnit / 2 anchors.rightMargin: Kirigami.Units.gridUnit / 2 spacing: Kirigami.Units.smallSpacing Kirigami.Theme.inherit: true Controls.TextField { id: input Kirigami.Theme.inherit: true Layout.fillWidth: true leftPadding: index.anchors.rightMargin rightPadding: index.width + 2 * index.anchors.rightMargin clip: true inputMethodHints: rootPage.privateMode ? Qt.ImhNoPredictiveText : Qt.ImhNone placeholderText: i18n("Search...") onAccepted: currentWebView.findInPageForward(displayText) onDisplayTextChanged: currentWebView.findInPageForward(displayText) Keys.onEscapePressed: findInPage.active = false Controls.Label { id: index anchors.right: parent.right anchors.rightMargin: Kirigami.Units.gridUnit / 2 anchors.verticalCenter: parent.verticalCenter text: "%1 / %2".arg(currentWebView.findInPageResultIndex).arg(currentWebView.findInPageResultCount) verticalAlignment: Text.AlignVCenter Kirigami.Theme.inherit: true color: Kirigami.Theme.disabledTextColor visible: input.displayText } } Controls.ToolButton { Kirigami.Theme.inherit: true Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "go-up" onClicked: currentWebView.findInPageBack(input.displayText) } Controls.ToolButton { Kirigami.Theme.inherit: true Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "go-down" onClicked: currentWebView.findInPageForward(input.displayText) } Controls.ToolButton { Kirigami.Theme.inherit: true Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "window-close" onClicked: findInPage.active = false } } states: [ State { name: "shown" when: findInPage.active AnchorChanges { target: findInPage anchors.bottom: findInPage.parent.bottom anchors.top: undefined } }, State { name: "hidden" AnchorChanges { target: findInPage anchors.bottom: undefined anchors.top: findInPage.parent.bottom } } ] onActiveChanged: { if (!active) input.text = ''; else input.forceActiveFocus(); } function activate() { active = true; } } angelfish-v21.07/src/contents/ui/History.qml000066400000000000000000000040271407527632300211020ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.8 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("History") Kirigami.ColumnView.fillWidth: false header: Item { anchors.horizontalCenter: parent.horizontalCenter height: Kirigami.Units.gridUnit * 3 width: list.width Kirigami.SearchField { id: search anchors.centerIn: parent width: parent.width - Kirigami.Units.gridUnit clip: true inputMethodHints: rootPage.privateMode ? Qt.ImhNoPredictiveText : Qt.ImhNone Kirigami.Theme.inherit: true onDisplayTextChanged: { if (displayText === "" || displayText.length > 2) { list.model.filter = displayText; timer.running = false; } else timer.running = true; } Keys.onEscapePressed: pageStack.pop() Timer { id: timer repeat: false interval: Math.max(1000, 3000 - search.displayText.length * 1000) onTriggered: list.model.filter = search.displayText } } } Component { id: delegateComponent UrlDelegate { highlightText: list.model.filter onClicked: { currentWebView.url = url; pageStack.pop(); } onRemoved: BrowserManager.removeFromHistory(url); } } ListView { id: list anchors.fill: parent interactive: height < contentHeight clip: true model: BookmarksHistoryModel { history: true } delegate: Kirigami.DelegateRecycler { width: list.width sourceComponent: delegateComponent } } Component.onCompleted: search.forceActiveFocus() } angelfish-v21.07/src/contents/ui/HistorySheet.qml000066400000000000000000000026121407527632300220710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 Simon Schmeisser // SPDX-FileCopyrightText: 2019 Jonah Brüchert // SPDX-FileCopyrightText: 2020 Rinigus // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 as Kirigami import org.kde.angelfish 1.0 Controls.Drawer { id: overlay dragMargin: 0 edge: Qt.BottomEdge width: parent.width property bool backHistory: true property int itemHeight: Kirigami.Units.gridUnit * 3 property int fullHeight: Math.min(Math.max(itemHeight * 1, listView.contentHeight) + itemHeight, 0.9 * rootPage.height) contentHeight: fullHeight contentWidth: parent.width contentItem: ListView { id: listView anchors.fill: parent boundsBehavior: Flickable.StopAtBounds clip: true delegate: UrlDelegate { showRemove: false onClicked: { currentWebView.goBackOrForward(model.offset); overlay.close(); } } model: overlay.backHistory ? currentWebView.navigationHistory.backItems : currentWebView.navigationHistory.forwardItems } onClosed: { currentWebView.forceActiveFocus(); } } angelfish-v21.07/src/contents/ui/InputSheet.qml000066400000000000000000000023711407527632300215310ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick.Controls 2.1 as Controls import QtQuick.Layouts 1.7 import QtQuick 2.7 import org.kde.kirigami 2.5 as Kirigami Kirigami.OverlaySheet { id: inputSheet property string placeholderText property string description property string title property string text signal accepted function accept() { inputSheet.text = sheetTextField.text inputSheet.close() accepted() } header: Kirigami.Heading { text: title } ColumnLayout { Controls.Label { Layout.fillWidth: true text: inputSheet.description wrapMode: Text.WordWrap } Controls.TextField { id: sheetTextField Layout.fillWidth: true placeholderText: inputSheet.placeholderText text: inputSheet.text focus: true onAccepted: accept() } Controls.Button { text: i18n("OK") Layout.alignment: Qt.AlignRight onClicked: accept() } } onSheetOpenChanged: { if (sheetOpen) { sheetTextField.forceActiveFocus() } } } angelfish-v21.07/src/contents/ui/Navigation.qml000066400000000000000000000245371407527632300215500ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // SPDX-FileCopyrightText: 2021 Devin Lin // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.15 import QtQuick.Layouts 1.0 import QtWebEngine 1.4 import QtQuick.Controls 2.0 as Controls import QtFeedback 5.0 import org.kde.kirigami 2.5 as Kirigami import org.kde.angelfish 1.0 Item { id: navigation height: expandedHeight property bool navigationShown: true property int expandedHeight: Kirigami.Units.gridUnit * 3 property int buttonSize: Kirigami.Units.gridUnit * 2 property int gestureThreshold: height * 2 property var tabsSheet signal activateUrlEntry; Rectangle { anchors.fill: parent; color: Kirigami.Theme.backgroundColor; } // left/right gesture icons Kirigami.Icon { id: leftGestureIcon anchors.margins: Kirigami.Units.gridUnit anchors.left: navigation.left anchors.top: navigation.top anchors.bottom: navigation.bottom anchors.verticalCenter: navigation.verticalCenter implicitWidth: height opacity: Math.abs(navContainer.x) / gestureThreshold source: "arrow-left" transform: Scale { origin.x: leftGestureIcon.implicitWidth / 2 origin.y: leftGestureIcon.implicitWidth / 2 xScale: Math.max(0, navContainer.x / gestureThreshold) yScale: Math.max(0, navContainer.x / gestureThreshold) } } Kirigami.Icon { id: rightGestureIcon anchors.margins: Kirigami.Units.gridUnit anchors.right: navigation.right anchors.top: navigation.top anchors.bottom: navigation.bottom anchors.verticalCenter: navigation.verticalCenter implicitWidth: height opacity: Math.abs(navContainer.x) / gestureThreshold source: "arrow-right" transform: Scale { origin.x: rightGestureIcon.implicitWidth / 2 origin.y: rightGestureIcon.implicitWidth / 2 xScale: Math.max(0, -navContainer.x / gestureThreshold) yScale: Math.max(0, -navContainer.x / gestureThreshold) } } Item { id: navContainer width: navigation.width height: navigation.height anchors.bottom: parent.bottom opacity: 1 - (Math.abs(navContainer.x) / (gestureThreshold * 2)) // left/right gestures HapticsEffect { id: vibrate intensity: 0.5 duration: Kirigami.Units.shortDuration } DragHandler { id: dragHandler target: parent yAxis.enabled: false xAxis.enabled: true xAxis.minimum: currentWebView.canGoForward ? -gestureThreshold : 0 xAxis.maximum: currentWebView.canGoBack ? gestureThreshold : 0 onActiveChanged: { xAnimator.restart(); // go back to center if (parent.x >= gestureThreshold && currentWebView.canGoBack) { currentWebView.goBack() } else if (parent.x <= -gestureThreshold && currentWebView.canGoForward) { currentWebView.goForward() } } } NumberAnimation on x { id: xAnimator running: !dragHandler.active duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad to: 0 } onXChanged: { if ((x >= gestureThreshold && currentWebView.canGoBack) || (x <= -gestureThreshold && currentWebView.canGoForward)) { vibrate.start(); } } RowLayout { id: layout anchors.fill: parent anchors.leftMargin: Kirigami.Units.gridUnit / 2 anchors.rightMargin: Kirigami.Units.gridUnit / 2 spacing: Kirigami.Units.smallSpacing Kirigami.Theme.inherit: true Controls.ToolButton { id: mainMenuButton icon.name: rootPage.privateMode ? "view-private" : "application-menu" visible: webBrowser.landscape || Settings.navBarMainMenu Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Kirigami.Theme.inherit: true onClicked: globalDrawer.open() } Controls.ToolButton { visible: webBrowser.landscape || Settings.navBarTabs Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize Rectangle { anchors.centerIn: parent height: Kirigami.Units.gridUnit * 1.25 width: Kirigami.Units.gridUnit * 1.25 color: "transparent" border.color: Kirigami.Theme.textColor border.width: Kirigami.Units.gridUnit / 10 radius: Kirigami.Units.gridUnit / 5 Kirigami.Theme.inherit: true Controls.Label { anchors.centerIn: parent height: Kirigami.Units.gridUnit width: Kirigami.Units.gridUnit fontSizeMode: Text.Fit minimumPixelSize: 0 minimumPointSize: 0 clip: true text: "%1".arg(tabs.count) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter Kirigami.Theme.inherit: true } } onClicked: tabsSheet.open() } Controls.ToolButton { id: backButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoBack && Settings.navBarBack icon.name: "go-previous" Kirigami.Theme.inherit: true onClicked: currentWebView.goBack() onPressAndHold: { historySheet.backHistory = true; historySheet.open(); } } Controls.ToolButton { id: forwardButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: currentWebView.canGoForward && Settings.navBarForward icon.name: "go-next" Kirigami.Theme.inherit: true onClicked: currentWebView.goForward() onPressAndHold: { historySheet.backHistory = false; historySheet.open(); } } Controls.ToolButton { id: labelItem Layout.fillWidth: true Layout.preferredHeight: buttonSize property string scheme: UrlUtils.urlScheme(currentWebView.requestedUrl) Controls.ToolButton { id: schemeIcon anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter icon.name: { if (labelItem.scheme === "https") return "lock"; if (labelItem.scheme === "http") return "unlock"; return ""; } visible: icon.name height: buttonSize * 0.75 width: visible ? buttonSize * 0.75 : 0 Kirigami.Theme.inherit: true background: Rectangle { implicitWidth: schemeIcon.width implicitHeight: schemeIcon.height color: "transparent" } onClicked: activateUrlEntry() } Controls.Label { anchors.left: schemeIcon.right anchors.right: parent.right anchors.top: parent.top height: parent.height text: { if (labelItem.scheme === "http" || labelItem.scheme === "https") { return UrlUtils.htmlFormattedUrl(currentWebView.requestedUrl) } return currentWebView.requestedUrl; } textFormat: Text.StyledText elide: Text.ElideRight verticalAlignment: Text.AlignVCenter Kirigami.Theme.inherit: true } onClicked: activateUrlEntry() } Controls.ToolButton { id: reloadButton Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: Settings.navBarReload icon.name: currentWebView.loading ? "process-stop" : "view-refresh" Kirigami.Theme.inherit: true onClicked: currentWebView.loading ? currentWebView.stopLoading() : currentWebView.reload() } Controls.ToolButton { id: optionsButton property string targetState: "overview" Layout.fillWidth: false Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize visible: webBrowser.landscape || Settings.navBarContextMenu icon.name: "overflow-menu" Kirigami.Theme.inherit: true onClicked: contextDrawer.open() } } } states: [ State { name: "shown" when: navigationShown AnchorChanges { target: navigation anchors.bottom: navigation.parent.bottom anchors.top: undefined } }, State { name: "hidden" when: !navigationShown AnchorChanges { target: navigation anchors.bottom: undefined anchors.top: navigation.parent.bottom } } ] transitions: Transition { AnchorAnimation { duration: navigation.visible ? Kirigami.Units.longDuration : 0 } } } angelfish-v21.07/src/contents/ui/NavigationEntrySheet.qml000066400000000000000000000106231407527632300235520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 Simon Schmeisser // SPDX-FileCopyrightText: 2019 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.2 import org.kde.kirigami 2.5 as Kirigami import org.kde.angelfish 1.0 import "regex-weburl.js" as RegexWebUrl Controls.Drawer { id: overlay dragMargin: 0 edge: Qt.BottomEdge width: parent.width bottomPadding: 0 topPadding: 0 rightPadding: 0 leftPadding: 0 property int buttonSize: Kirigami.Units.gridUnit * 2 property int fullHeight: 0.9 * rootPage.height property bool openedState: false contentHeight: fullHeight - topPadding - bottomPadding contentWidth: parent.width - rightPadding - leftPadding contentItem: Item { width: parent.width height: parent.height RowLayout { id: editRow anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter height: Kirigami.Units.gridUnit * 3 width: parent.width - Kirigami.Units.gridUnit Controls.ToolButton { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "window-minimize" Kirigami.Theme.inherit: true onClicked: overlay.close() } Controls.TextField { id: urlInput Layout.fillWidth: true clip: true focus: false inputMethodHints: rootPage.privateMode ? Qt.ImhNoPredictiveText : Qt.ImhNone Kirigami.Theme.inherit: true onActiveFocusChanged: if (activeFocus) selectAll() onAccepted: applyUrl() onDisplayTextChanged: { if (!openedState) return; // avoid filtering if (displayText === "" || displayText.length > 2) { urlFilter.filter = displayText; timer.running = false; } else timer.running = true; } Keys.onEscapePressed: if (overlay.sheetOpen) overlay.close() Timer { id: timer repeat: false interval: Math.max(1000, 3000 - urlInput.displayText.length * 1000) onTriggered: urlFilter.filter = urlInput.displayText } function applyUrl() { if (text.match(RegexWebUrl.re_weburl)) { currentWebView.url = UrlUtils.urlFromUserInput(text); } else { currentWebView.url = UrlUtils.urlFromUserInput(Settings.searchBaseUrl + text); } overlay.close(); } } Controls.ToolButton { Layout.preferredWidth: buttonSize Layout.preferredHeight: buttonSize icon.name: "go-next" Kirigami.Theme.inherit: true onClicked: urlInput.applyUrl(); } } ListView { id: listView anchors { bottom: parent.bottom left: parent.left right: parent.right top: editRow.bottom } boundsBehavior: Flickable.StopAtBounds clip: true delegate: UrlDelegate { showRemove: false onClicked: { currentWebView.url = url; overlay.close(); } highlightText: urlFilter.filter width: parent && parent.width } model: BookmarksHistoryModel { id: urlFilter active: openedState bookmarks: true history: true } } } onOpened: { // check if the drawer was just slightly slided if (openedState) return; urlInput.text = currentWebView.requestedUrl; urlInput.forceActiveFocus(); urlInput.selectAll(); urlFilter.filter = ""; openedState = true; listView.positionViewAtBeginning(); } onClosed: { openedState = false; currentWebView.forceActiveFocus(); } } angelfish-v21.07/src/contents/ui/NewTabQuestion.qml000066400000000000000000000012341407527632300223460ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.0 import org.kde.kirigami 2.4 as Kirigami Kirigami.InlineMessage { id: newTabQuestion type: Kirigami.MessageType.Warning text: url ? i18n("Site wants to open a new tab: \n%1", url.toString()) : "" showCloseButton: true property url url actions: [ Kirigami.Action { icon.name: "tab-new" text: i18n("Open") onTriggered: { tabs.tabsModel.newTab(newTabQuestion.url.toString()) newTabQuestion.visible = false } } ] } angelfish-v21.07/src/contents/ui/SettingsAdblock.qml000066400000000000000000000062661407527632300225300ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.7 import QtQuick.Layouts 1.0 import QtQuick.Controls 2.5 as Controls import org.kde.kirigami 2.12 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { id: adblockSettings enabled: AdblockUrlInterceptor.adblockSupported title: i18n("Adblock settings") Kirigami.ColumnView.fillWidth: false actions.main: Kirigami.Action { icon.name: "list-add" onTriggered: addSheet.open() } supportsRefreshing: true onRefreshingChanged: { if (refreshing) { filterlistModel.refreshLists(); } } Kirigami.PlaceholderMessage { anchors.centerIn: parent visible: !AdblockUrlInterceptor.adblockSupported width: parent.width - (Kirigami.Units.largeSpacing * 4) text: i18n("The adblock functionality isn't included in this build.") } Kirigami.OverlaySheet { id: addSheet header: Kirigami.Heading { text: i18n("Add filterlist") } contentItem: ColumnLayout { Layout.preferredWidth: adblockSettings.width Controls.Label { Layout.fillWidth: true text: i18n("Name") } Controls.TextField { id: nameInput Layout.fillWidth: true } Controls.Label { Layout.fillWidth: true text: i18n("Url") } Controls.TextField { id: urlInput Layout.fillWidth: true } Controls.Button { Layout.alignment: Qt.AlignRight text: i18n("Add") onClicked: { filterlistModel.addFilterList(nameInput.text, urlInput.text) adblockSettings.refreshing = true filterlistModel.refreshLists() addSheet.close() } } } } ListView { visible: AdblockUrlInterceptor.adblockSupported model: AdblockFilterListsModel { id: filterlistModel onRefreshFinished: adblockSettings.refreshing = false } delegate: Kirigami.SwipeListItem { id: delegateRoot required property string displayName required property url url required property int index ColumnLayout { Kirigami.Heading { Layout.fillWidth: true level: 3 elide: Qt.ElideRight text: delegateRoot.displayName } Controls.Label { Layout.fillWidth: true elide: Qt.ElideRight text: delegateRoot.url } } text: displayName actions: [ Kirigami.Action { icon.name: "list-remove" text: i18n("Remove this filter list") onTriggered: filterlistModel.removeFilterList(delegateRoot.index) } ] } } } angelfish-v21.07/src/contents/ui/SettingsNavigationBarPage.qml000066400000000000000000000101711407527632300245000ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Rinigus // SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Layouts 1.11 import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Navigation bar") topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 Kirigami.ColumnView.fillWidth: false background: Rectangle { Kirigami.Theme.colorSet: Kirigami.Theme.View color: Kirigami.Theme.backgroundColor } ColumnLayout { spacing: 0 property real itemHeight: Kirigami.Units.gridUnit * 2.5 Controls.Label { text: i18n("Choose the buttons enabled in navigation bar. \ Some of the buttons can be hidden only in portrait \ orientation of the browser and are always shown if \ the browser is wider than its height.\n\n \ Note that if you disable the menu buttons, you \ will be able to access the menus either by swiping \ from the left or right side or to a side along the bottom \ of the window.") Layout.fillWidth: true padding: Kirigami.Units.gridUnit wrapMode: Text.WordWrap } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Main menu in portrait") Layout.fillWidth: true checked: Settings.navBarMainMenu leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarMainMenu = checked } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Tabs in portrait") Layout.fillWidth: true checked: Settings.navBarTabs leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarTabs = checked } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Context menu in portrait") Layout.fillWidth: true checked: Settings.navBarContextMenu leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarContextMenu = checked } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Go back") Layout.fillWidth: true checked: Settings.navBarBack leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarBack = checked } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Go forward") Layout.fillWidth: true checked: Settings.navBarForward leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarForward = checked } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Reload/Stop") Layout.fillWidth: true checked: Settings.navBarReload leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: parent.itemHeight onCheckedChanged: Settings.navBarReload = checked } Kirigami.Separator { Layout.fillWidth: true } Kirigami.Separator { Layout.fillWidth: true } Item { Layout.fillHeight: true } } } angelfish-v21.07/src/contents/ui/SettingsPage.qml000066400000000000000000000064631407527632300220440ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Layouts 1.11 import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Settings") topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 Kirigami.ColumnView.fillWidth: false background: Rectangle { Kirigami.Theme.colorSet: Kirigami.Theme.View color: Kirigami.Theme.backgroundColor } ColumnLayout { id: settingsPage spacing: 0 Controls.SwitchDelegate { text: i18n("Enable JavaScript") Layout.fillWidth: true checked: Settings.webJavaScriptEnabled onClicked: Settings.webJavaScriptEnabled = checked leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 } Kirigami.Separator { Layout.fillWidth: true } Controls.SwitchDelegate { text: i18n("Load images") Layout.fillWidth: true checked: Settings.webAutoLoadImages onClicked: Settings.webAutoLoadImages = checked leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 } Kirigami.Separator { Layout.fillWidth: true } Controls.ItemDelegate { text: i18n("Search Engine") Layout.fillWidth: true onClicked: pageStack.push(Qt.resolvedUrl("SettingsSearchEnginePage.qml")) leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 } Kirigami.Separator { Layout.fillWidth: true } Controls.ItemDelegate { text: i18n("Navigation bar") Layout.fillWidth: true leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 onClicked: pageStack.push(Qt.resolvedUrl("SettingsNavigationBarPage.qml")) } Kirigami.Separator { Layout.fillWidth: true } Controls.ItemDelegate { text: i18n("Adblock filter lists") Layout.fillWidth: true leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 onClicked: pageStack.push(Qt.resolvedUrl("SettingsAdblock.qml")) } Kirigami.Separator { Layout.fillWidth: true } Controls.ItemDelegate { text: i18n("Web Apps") Layout.fillWidth: true leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 onClicked: pageStack.push(Qt.resolvedUrl("SettingsWebApps.qml")) } Kirigami.Separator { Layout.fillWidth: true } Item { Layout.fillHeight: true } } } angelfish-v21.07/src/contents/ui/SettingsSearchEnginePage.qml000066400000000000000000000104241407527632300243100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Rinigus // SPDX-FileCopyrightText: 2020 Jonah Brüchert // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Layouts 1.11 import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Search Engine") topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 Kirigami.ColumnView.fillWidth: false background: Rectangle { Kirigami.Theme.colorSet: Kirigami.Theme.View color: Kirigami.Theme.backgroundColor } property string baseUrl: Settings.searchBaseUrl ColumnLayout { id: list spacing: 0 property string customName: i18n("Custom") // custom search engine input sheet InputSheet { id: searchEnginePopup title: i18n("Search Engine") description: i18n("Base URL of your preferred search engine") text: Settings.searchCustomUrl onAccepted: { const url = UrlUtils.urlFromUserInput(searchEnginePopup.text); Settings.searchCustomUrl = url; baseUrl = url; searchEngines.setProperty(searchEngines.count - 1, "url", url); } } Kirigami.Separator { Layout.fillWidth: true } Repeater { model: ListModel { id: searchEngines ListElement { title: "Bing" url: "https://www.bing.com/search?q=" } ListElement { title: "DuckDuckGo" url: "https://start.duckduckgo.com/?q=" } ListElement { title: "Ecosia" url: "https://www.ecosia.org/search?q=" } ListElement { title: "Google" url: "https://www.google.com/search?q=" } ListElement { title: "Lilo" url: "https://search.lilo.org/searchweb.php?q=" } ListElement { title: "Peekier" url: "https://peekier.com/#!" } ListElement { title: "Qwant" url: "https://www.qwant.com/?q=" } ListElement { title: "Qwant Junior" url: "https://www.qwantjunior.com/?q=" } ListElement { title: "StartPage" url: "https://www.startpage.com/do/dsearch?query=" } ListElement { title: "Swisscows" url: "https://swisscows.com/web?query=" } ListElement { title: "Wikipedia" url: "https://wikipedia.org/wiki/Special:Search?search=" } } delegate: ColumnLayout { spacing: 0 Controls.RadioDelegate { leftPadding: Kirigami.Units.gridUnit rightPadding: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit * 2.5 Layout.fillWidth: true checked: model.url === baseUrl text: model.title onClicked: { if (model.title !== list.customName) baseUrl = model.url; else { searchEnginePopup.open(); } // restore property binding checked = Qt.binding(function() { return (model.url === baseUrl) }); } } Kirigami.Separator { Layout.fillWidth: true } } } } onBaseUrlChanged: { Settings.searchBaseUrl = UrlUtils.urlFromUserInput(baseUrl); } Component.onCompleted: { searchEngines.append({ "title": list.customName, "url": Settings.searchCustomUrl }); } } angelfish-v21.07/src/contents/ui/SettingsWebApps.qml000066400000000000000000000023321407527632300225200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.4 as Controls import QtQuick.Layouts 1.11 import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Kirigami.ScrollablePage { title: i18n("Web Apps") ListView { model: WebAppManagerModel { id: webAppModel } delegate: Kirigami.SwipeListItem { required property int index; required property string desktopIcon; required property string name; required property string url; RowLayout { spacing: Kirigami.Units.largeSpacing Kirigami.Icon { source: desktopIcon } Controls.Label { Layout.fillWidth: true text: name elide: Text.ElideRight } } actions: [ Kirigami.Action { text: i18n("Remove app") icon.name: "delete" onTriggered: webAppModel.removeApp(index) } ] } } } angelfish-v21.07/src/contents/ui/ShareSheet.qml000066400000000000000000000020431407527632300214700ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2019 Nicolas Fella // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick.Controls 2.1 as Controls import QtQuick.Layouts 1.7 import QtQuick 2.7 import QtQuick.Window 2.15 import org.kde.kirigami 2.5 as Kirigami import org.kde.purpose 1.0 as Purpose Kirigami.OverlaySheet { id: inputSheet property url url property string title header: Kirigami.Heading { text: i18n("Share page") } Purpose.AlternativesView { id: view pluginType: "ShareUrl" clip: true delegate: Kirigami.BasicListItem { label: model.display icon: "arrow-right" onClicked: view.createJob (model.index) Keys.onReturnPressed: view.createJob (model.index) Keys.onEnterPressed: view.createJob (model.index) } onFinished: close() } onSheetOpenChanged: { view.inputData = { "urls": [inputSheet.url.toString()], "title": inputSheet.title } } } angelfish-v21.07/src/contents/ui/Tabs.qml000066400000000000000000000263071407527632300203370ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // SPDX-FileCopyrightText: 2021 Devin Lin // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.15 import QtQuick.Controls 2.15 as Controls import QtGraphicalEffects 1.0 //import QtWebEngine 1.0 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.15 as Kirigami import org.kde.angelfish 1.0 Kirigami.OverlayDrawer { id: tabsRoot height: contents.implicitHeight + Kirigami.Units.largeSpacing width: webBrowser.width edge: Qt.BottomEdge parent: applicationWindow().overlay onClosed: tabsSheetLoader.active = false // unload tabs when the sheet is closed property int itemHeight: Kirigami.Units.gridUnit * 6 property int itemWidth: { if (!landscapeMode) return width - Kirigami.Units.smallSpacing * 2; // using grid width to take into account its scrollbar const n = Math.floor((grid.width - Kirigami.Units.largeSpacing) / (landscapeMinWidth + Kirigami.Units.largeSpacing)); return Math.floor(grid.width / n) - Kirigami.Units.largeSpacing; } property int landscapeMinWidth: Kirigami.Units.gridUnit * 12 property bool landscapeMode: grid.width > landscapeMinWidth * 2 + 3 * Kirigami.Units.largeSpacing //Rectangle { anchors.fill: parent; color: "brown"; opacity: 0.5; } ColumnLayout { id: contents anchors.left: parent.left anchors.right: parent.right spacing: 0 Kirigami.Icon { Layout.margins: Kirigami.Units.smallSpacing source: "arrow-down" implicitWidth: Kirigami.Units.gridUnit implicitHeight: Kirigami.Units.gridUnit Layout.alignment: Qt.AlignHCenter } RowLayout { Layout.leftMargin: Kirigami.Units.smallSpacing Layout.rightMargin: Kirigami.Units.smallSpacing Layout.bottomMargin: Kirigami.Units.smallSpacing Kirigami.Heading { level: 1 text: rootPage.privateMode ? i18n("Private Tabs") : i18n("Tabs") } Item { Layout.fillWidth: true } Controls.ToolButton { icon.name: "list-add" text: i18n("New Tab") onClicked: { tabs.tabsModel.newTab("about:blank") urlEntry.open(); tabsRoot.close(); } } z: 1 } Controls.ScrollView { id: scrollView Layout.fillWidth: true Layout.minimumHeight: Kirigami.Units.gridUnit * 12 Layout.preferredHeight: applicationWindow().height * 0.6 Layout.bottomMargin: Kirigami.Units.smallSpacing Layout.topMargin: Kirigami.Units.largeSpacing Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff GridView { id: grid model: tabs.model cellWidth: itemWidth + (landscapeMode ? Kirigami.Units.largeSpacing : 0) cellHeight: itemHeight + Kirigami.Units.largeSpacing clip: true add: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: Kirigami.Units.shortDuration } } remove: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: Kirigami.Units.shortDuration } } displaced: Transition { NumberAnimation { properties: "x,y"; duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad} } delegate: Controls.ItemDelegate { id: gridItem // taking care of spacing width: grid.cellWidth height: grid.cellHeight property int sourceX: (index % (grid.width / grid.cellWidth)) * grid.cellWidth DragHandler { id: dragHandler target: parent yAxis.enabled: false xAxis.enabled: true onActiveChanged: { xAnimator.stop(); let rightThreshold = Math.min(gridItem.sourceX + grid.width * 0.5, grid.width + Kirigami.Units.gridUnit * 2); let leftThreshold = Math.max(gridItem.sourceX - grid.width * 0.5, - Kirigami.Units.gridUnit * 2); if (parent.x > rightThreshold) { xAnimator.to = grid.width; } else if (parent.x < leftThreshold) { xAnimator.to = -grid.width; } else { xAnimator.to = gridItem.sourceX; } xAnimator.start(); } } NumberAnimation on x { id: xAnimator running: !dragHandler.active duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad to: gridItem.sourceX onFinished: { if (to != gridItem.sourceX) { // close tab tabs.tabsModel.closeTab(index); } } } onClicked: { print("Switch from " + tabs.currentIndex + " to tab " + index); tabs.currentIndex = index; tabsRoot.close(); } background: Item {} Item { id: tabItem anchors.centerIn: parent width: itemWidth height: itemHeight // ShaderEffectSource requires that corresponding WebEngineView is // visible. Here, visibility is enabled while snapshot is taken and // removed as soon as it is ready. ShaderEffectSource { id: shaderItem live: false anchors.fill: parent sourceRect: Qt.rect(0, 0, sourceItem.width, height/width * sourceItem.width) sourceItem: tabs.itemAt(index) Component.onCompleted: { sourceItem.readyForSnapshot = true; scheduleUpdate(); } onScheduledUpdateCompleted: sourceItem.readyForSnapshot = false LinearGradient { id: grad anchors.fill: parent cached: true start: Qt.point(0,0) end: Qt.point(0,height) gradient: Gradient { GradientStop { position: 0.4; color: "transparent"; } GradientStop { position: 1.3; color: "black"; } } } } Rectangle { // border around a selected tile anchors.fill: parent; border.color: Kirigami.Theme.disabledTextColor border.width: webBrowser.borderWidth color: "transparent" opacity: tabs.currentIndex === index ? 1.0 : 0.2 } Rectangle { // selection indicator anchors.fill: parent color: gridItem.pressed ? Kirigami.Theme.highlightColor : "transparent" opacity: 0.2 } Controls.ToolButton { icon.name: "window-close" height: Kirigami.Units.gridUnit * 2 width: height anchors.right: parent.right anchors.rightMargin: Kirigami.Units.smallSpacing + Kirigami.Units.largeSpacing + (tabsRoot.landscapeMode ? 0 : tabsRoot.width-grid.width) anchors.top: parent.top anchors.topMargin: Kirigami.Units.smallSpacing onClicked: tabs.tabsModel.closeTab(index) } Column { id: label anchors { left: tabItem.left right: tabItem.right bottom: tabItem.bottom bottomMargin: Kirigami.Units.smallSpacing leftMargin: Kirigami.Units.largeSpacing rightMargin: Kirigami.Units.largeSpacing } spacing: 0 Kirigami.Heading { id: heading elide: Text.ElideRight level: 4 text: tabs.itemAt(index) ? tabs.itemAt(index).title : "" width: label.width color: "white" } Controls.Label { elide: Text.ElideRight font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.5 text: tabs.itemAt(index) ? tabs.itemAt(index).url : "" width: label.width color: "white" visible: heading.text === "" } } Image { anchors { bottom: tabItem.bottom right: tabItem.right bottomMargin: Kirigami.Units.smallSpacing rightMargin: Kirigami.Units.smallSpacing + Kirigami.Units.largeSpacing + (tabsRoot.landscapeMode ? 0 : tabsRoot.width-grid.width) } fillMode: Image.PreserveAspectFit height: Math.min(sourceSize.height, Kirigami.Units.gridUnit * 2) source: tabs.itemAt(index) ? tabs.itemAt(index).icon : "" } } } } } } Component.onCompleted: grid.currentIndex = tabs.currentIndex } angelfish-v21.07/src/contents/ui/UrlDelegate.qml000066400000000000000000000035571407527632300216450ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.3 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.3 import org.kde.kirigami 2.5 as Kirigami Kirigami.SwipeListItem { id: urlDelegate property bool showRemove: true property string highlightText property var regex: new RegExp(highlightText, 'i') property string highlightedText: "$&" height: Kirigami.Units.gridUnit * 3 Kirigami.Theme.colorSet: Kirigami.Theme.View onClicked: { currentWebView.url = url; } signal removed RowLayout { Kirigami.Theme.inherit: true Item { Layout.preferredHeight: parent.height Layout.preferredWidth: parent.height Image { anchors.fill: parent fillMode: Image.PreserveAspectFit source: model && model.icon ? model.icon : "" } } ColumnLayout { Layout.fillWidth: true // title Controls.Label { text: title ? (highlightText ? title.replace(regex, highlightedText) : title) : "" elide: Qt.ElideRight maximumLineCount: 1 Layout.fillWidth: true } // url Controls.Label { text: url ? (highlightText ? url.replace(regex, highlightedText) : url) : "" opacity: 0.6 elide: Qt.ElideRight maximumLineCount: 1 Layout.fillWidth: true } } } actions: [ Kirigami.Action { icon.name: "list-remove" visible: urlDelegate.showRemove onTriggered: urlDelegate.removed(); } ] } angelfish-v21.07/src/contents/ui/webbrowser.qml000066400000000000000000000407561407527632300216330ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler // // SPDX-License-Identifier: GPL-2.0-or-later import QtQuick 2.1 import QtWebEngine 1.6 import QtQuick.Window 2.3 import QtGraphicalEffects 1.0 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 as Controls import org.kde.kirigami 2.7 as Kirigami import org.kde.angelfish 1.0 Kirigami.ApplicationWindow { id: webBrowser title: i18n("Angelfish Web Browser") /** Pointer to the currently active view. * * Browser-level functionality should use this to refer to the current * view, rather than looking up views in the mode, as far as possible. */ property WebView currentWebView: tabs.currentItem // Pointer to the currently active list of tabs. // // As there are private and normal tabs, switch between // them according to the current mode. property ListWebView tabs: rootPage.privateMode ? privateTabs : regularTabs // Used to determine if the window is in landscape mode property bool landscape: width > height onCurrentWebViewChanged: { print("Current WebView is now : " + tabs.currentIndex); } property int borderWidth: Math.round(Kirigami.Units.gridUnit / 18); property color borderColor: Kirigami.Theme.highlightColor; x: Settings.windowX y: Settings.windowY width: Settings.windowWidth height: Settings.windowHeight pageStack.globalToolBar.showNavigationButtons: { if (pageStack.depth <= 1) return Kirigami.ApplicationHeaderStyle.None; if (pageStack.currentIndex === pageStack.depth - 1) return Kirigami.ApplicationHeaderStyle.ShowBackButton; // not used so far, but maybe in future return (Kirigami.ApplicationHeaderStyle.ShowBackButton | Kirigami.ApplicationHeaderStyle.ShowForwardButton); } globalDrawer: Kirigami.GlobalDrawer { id: globalDrawer handleVisible: false actions: [ Kirigami.Action { icon.name: "tab-duplicate" onTriggered: { popSubPages(); tabsSheetLoader.open(); } text: i18n("Tabs") }, Kirigami.Action { icon.name: "view-private" onTriggered: { rootPage.privateMode ? rootPage.privateMode = false : rootPage.privateMode = true } text: rootPage.privateMode ? i18n("Leave private mode") : i18n("Private mode") }, Kirigami.Action { icon.name: "bookmarks" onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("Bookmarks.qml")) } text: i18n("Bookmarks") }, Kirigami.Action { icon.name: "view-history" onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("History.qml")) } text: i18n("History") }, Kirigami.Action { icon.name: "download" text: i18n("Downloads") onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("Downloads.qml")) } }, Kirigami.Action { icon.name: "configure" text: i18n("Settings") onTriggered: { popSubPages(); pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) } }, Kirigami.Action { visible: AdblockUrlInterceptor.adblockSupported enabled: AdblockUrlInterceptor.adblockSupported id: adblockAction icon.name: "cards-block" text: i18n("Enable Adblock") checkable: true checked: AdblockUrlInterceptor.enabled onCheckedChanged: { AdblockUrlInterceptor.enabled = adblockAction.checked } } ] } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer handleVisible: false } // Main Page pageStack.initialPage: Kirigami.Page { id: rootPage leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None Kirigami.ColumnView.fillWidth: true Kirigami.ColumnView.pinned: true Kirigami.ColumnView.preventStealing: true // Required to enforce active tab reload // on start. As a result, mixed isMobile // tabs will work correctly property bool initialized: false property bool privateMode: false // Used for automatically show or hid navigation // bar. Set separately to combine with other options // for navigation bar management (webapp and others) property bool navigationAutoShow: true property alias questionLoader: questionLoader ListWebView { id: regularTabs objectName: "regularTabsObject" anchors.fill: parent activeTabs: rootPage.initialized && !rootPage.privateMode } ListWebView { id: privateTabs anchors.fill: parent activeTabs: rootPage.initialized && rootPage.privateMode privateTabsMode: true } Controls.ScrollBar { visible: true anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom position: currentWebView.scrollPosition.y / currentWebView.contentsSize.height orientation: Qt.Vertical size: currentWebView.height / currentWebView.contentsSize.height interactive: false } ErrorHandler { id: errorHandler errorString: currentWebView.errorString errorCode: currentWebView.errorCode anchors { top: parent.top left: parent.left right: parent.right bottom: navigation.top } visible: currentWebView.errorCode !== "" onRefreshRequested: currentWebView.reload() } Loader { id: questionLoader Component.onCompleted: { if (AdblockUrlInterceptor.adblockSupported && AdblockUrlInterceptor.downloadNeeded) { questionLoader.setSource("AdblockFilterDownloadQuestion.qml") } } anchors.bottom: navigation.top anchors.left: parent.left anchors.right: parent.right } // Container for the progress bar Item { id: progressItem height: Math.round(Kirigami.Units.gridUnit / 6) z: navigation.z + 1 anchors { bottom: findInPage.active ? findInPage.top : navigation.top bottomMargin: -Math.round(height / 2) left: tabs.left right: tabs.right } opacity: currentWebView.loading ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad; } } Rectangle { color: Kirigami.Theme.highlightColor width: Math.round((currentWebView.loadProgress / 100) * parent.width) anchors { top: parent.top left: parent.left bottom: parent.bottom } } } Loader { id: sheetLoader } // Unload the ShareSheet again after it closed Connections { target: sheetLoader.item function onSheetOpenChanged() { if (!sheetLoader.item.sheetOpen) { sheetLoader.source = "" } } } UrlObserver { id: urlObserver url: currentWebView.url } WebAppCreator { id: webAppCreator websiteName: currentWebView.title } // The menu at the bottom right contextualActions: [ Kirigami.Action { icon.name: "edit-find" shortcut: "Ctrl+F" onTriggered: findInPage.activate() text: i18n("Find in page") }, Kirigami.Action { icon.name: "document-share" text: i18n("Share page") onTriggered: { sheetLoader.setSource("ShareSheet.qml") sheetLoader.item.url = currentWebView.url sheetLoader.item.title = currentWebView.title sheetLoader.item.open() } }, Kirigami.Action { id: addHomeScreenAction icon.name: "list-add" text: i18n("Add to homescreen") enabled: !webAppCreator.exists onTriggered: { webAppCreator.createDesktopFile(currentWebView.title, currentWebView.url, currentWebView.icon) } }, Kirigami.Action { icon.name: "application-x-object" text: i18n("Open in app") onTriggered: { Qt.openUrlExternally(currentWebView.url) } }, Kirigami.Action { enabled: currentWebView.canGoBack icon.name: "go-previous" text: i18n("Go previous") onTriggered: { currentWebView.goBack() } }, Kirigami.Action { enabled: currentWebView.canGoForward icon.name: "go-next" text: i18n("Go forward") onTriggered: { currentWebView.goForward() } }, Kirigami.Action { icon.name: currentWebView.loading ? "process-stop" : "view-refresh" text: currentWebView.loading ? i18n("Stop loading") : i18n("Refresh") onTriggered: { currentWebView.loading ? currentWebView.stopLoading() : currentWebView.reload() } }, Kirigami.Action { id: bookmarkAction checkable: true checked: urlObserver.bookmarked icon.name: "bookmarks" text: checked ? i18n("Bookmarked") : i18n("Bookmark") onTriggered: { if (checked) { var request = { url: currentWebView.url, title: currentWebView.title, icon: currentWebView.icon } BrowserManager.addBookmark(request); } else { BrowserManager.removeBookmark(currentWebView.url); } } }, Kirigami.Action { icon.name: "computer" text: i18n("Show desktop site") checkable: true checked: !currentWebView.userAgent.isMobile onTriggered: { currentWebView.userAgent.isMobile = !currentWebView.userAgent.isMobile; } }, Kirigami.Action { icon.name: "edit-select-text" text: rootPage.navigationAutoShow ? i18n("Hide navigation bar") : i18n("Show navigation bar") visible: navigation.visible onTriggered: { if (!navigation.visible) return; rootPage.navigationAutoShow = !rootPage.navigationAutoShow; } } ] // Tabs sheet Loader { id: tabsSheetLoader active: false function open() { active = true; item.open(); } sourceComponent: Tabs {} } // Find bar FindInPageBar { id: findInPage Kirigami.Theme.colorSet: rootPage.privateMode ? Kirigami.Theme.Complementary : Kirigami.Theme.Window layer.enabled: active layer.effect: DropShadow { verticalOffset: - 1 color: Kirigami.Theme.disabledTextColor samples: 10 spread: 0.1 cached: true // element is static } } // Bottom navigation bar Navigation { id: navigation anchors { bottom: parent.bottom left: parent.left right: parent.right } navigationShown: visible && rootPage.navigationAutoShow visible: webBrowser.visibility !== Window.FullScreen && !findInPage.active tabsSheet: tabsSheetLoader Kirigami.Theme.colorSet: rootPage.privateMode ? Kirigami.Theme.Complementary : Kirigami.Theme.Window layer.enabled: navigation.navigationShown layer.effect: DropShadow { verticalOffset: - 1 color: Kirigami.Theme.disabledTextColor samples: 10 spread: 0.1 cached: true // element is static } onActivateUrlEntry: urlEntry.open() } NavigationEntrySheet { id: urlEntry } HistorySheet { id: historySheet } // Thin line above navigation or find Rectangle { height: webBrowser.borderWidth color: webBrowser.borderColor anchors { left: parent.left bottom: findInPage.active ? findInPage.top : navigation.top right: parent.right } visible: navigation.navigationShown || findInPage.active } // dealing with hiding and showing navigation bar property point oldScrollPosition: Qt.point(0, 0) property bool pageAlmostReady: !currentWebView.loading || currentWebView.loadProgress > 90 onPageAlmostReadyChanged: { if (!rootPage.pageAlmostReady) rootPage.navigationAutoShow = true; else rootPage.oldScrollPosition = currentWebView.scrollPosition; } Connections { target: currentWebView function onScrollPositionChanged() { var delta = 100; if (rootPage.navigationAutoShow && rootPage.pageAlmostReady) { if (rootPage.oldScrollPosition.y + delta < currentWebView.scrollPosition.y) { // hide navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = false; } else if (rootPage.oldScrollPosition.y > currentWebView.scrollPosition.y) { // navbar open and scrolling up rootPage.oldScrollPosition = currentWebView.scrollPosition; } } else if (!rootPage.navigationAutoShow) { if (rootPage.oldScrollPosition.y - delta > currentWebView.scrollPosition.y) { // show navbar rootPage.oldScrollPosition = currentWebView.scrollPosition; rootPage.navigationAutoShow = true; } else if (rootPage.oldScrollPosition.y < currentWebView.scrollPosition.y) { // navbar closed and scrolling down rootPage.oldScrollPosition = currentWebView.scrollPosition; } } } } } Connections { target: webBrowser.pageStack function onCurrentIndexChanged() { // drop all sub pages as soon as the browser window is the // focussed one if (webBrowser.pageStack.currentIndex === 0) popSubPages(); } } // Store window dimensions Component.onCompleted: { rootPage.initialized = true } function popSubPages() { while (webBrowser.pageStack.depth > 1) webBrowser.pageStack.pop(); } } angelfish-v21.07/src/downloadsmodel.cpp000066400000000000000000000035031407527632300201710ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "downloadsmodel.h" #include #include #include #include #include "downloadmanager.h" #include "qquickwebenginedownloaditem.h" DownloadsModel::DownloadsModel(QObject *parent) : QAbstractListModel(parent) { } QVariant DownloadsModel::data(const QModelIndex &index, int role) const { const auto &downloads = DownloadManager::instance().downloads(); switch (role) { case Role::FileNameRole: return downloads.at(index.row())->downloadFileName(); case Role::UrlRole: return downloads.at(index.row())->url(); case Role::DownloadRole: return QVariant::fromValue(downloads.at(index.row())); case Role::MimeTypeIconRole: { static auto mimeDB = QMimeDatabase(); return mimeDB.mimeTypeForName(downloads.at(index.row())->mimeType()).iconName(); } case Role::DownloadedFilePathRole: { const QQuickWebEngineDownloadItem *download = downloads.at(index.row()); return QUrl(QStringLiteral("file://") + download->downloadDirectory() + QDir::separator() + download->downloadFileName()); } } return {}; } int DownloadsModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : DownloadManager::instance().downloads().size(); } QHash DownloadsModel::roleNames() const { return { {UrlRole, "url"}, {FileNameRole, "fileName"}, {DownloadRole, "download"}, {MimeTypeIconRole, "mimeTypeIcon"}, {DownloadedFilePathRole, "downloadedFilePath"}, }; } void DownloadsModel::removeDownload(const int index) { beginRemoveRows({}, index, index); DownloadManager::instance().removeDownload(index); endRemoveRows(); } angelfish-v21.07/src/downloadsmodel.h000066400000000000000000000012711407527632300176360ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include class DownloadsModel : public QAbstractListModel { Q_OBJECT enum Role { UrlRole, FileNameRole, DownloadRole, MimeTypeIconRole, DownloadedFilePathRole, }; public: explicit DownloadsModel(QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = {}) const override; QHash roleNames() const override; Q_INVOKABLE void removeDownload(const int index); }; angelfish-v21.07/src/main.cpp000066400000000000000000000163471407527632300161140ustar00rootroot00000000000000/* SPDX-FileCopyrightText: 2019 Jonah Brüchert SPDX-License-Identifier: LGPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include #include #include "adblockfilterlistsmanager.h" #include "adblockfilterlistsmodel.h" #include "adblockurlinterceptor.h" #include "angelfishsettings.h" #include "angelfishwebprofile.h" #include "bookmarkshistorymodel.h" #include "browsermanager.h" #include "downloadsmodel.h" #include "iconimageprovider.h" #include "tabsmodel.h" #include "urlobserver.h" #include "urlutils.h" #include "useragent.h" #include "version.h" #include "webappcreator.h" #include "webappmanagermodel.h" constexpr auto APPLICATION_ID = "org.kde.angelfish"; Q_DECL_EXPORT int main(int argc, char *argv[]) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Setup QtWebEngine qputenv("QTWEBENGINE_DIALOG_SET", "QtQuickControls2"); #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif QApplication app(argc, argv); QCoreApplication::setOrganizationName(QStringLiteral("KDE")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QCoreApplication::setApplicationName(QStringLiteral("angelfish")); QCoreApplication::setApplicationVersion(QStringLiteral(ANGELFISH_VERSION_STRING)); KLocalizedString::setApplicationDomain("angelfish"); #if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0) QtWebEngine::initialize(); #endif // Command line parser QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("url"), i18n("URL to open"), QStringLiteral("[url]")); parser.addHelpOption(); parser.addVersionOption(); parser.process(app); // QML loading QQmlApplicationEngine engine; // Open links in the already running window when e.g clicked on in another application. KDBusService service(KDBusService::Unique, &app); QObject::connect(&service, &KDBusService::activateRequested, &app, [&parser, &engine](const QStringList &arguments) { parser.parse(arguments); if (!parser.positionalArguments().isEmpty()) { const QString initialUrl = QUrl::fromUserInput(parser.positionalArguments().constFirst()).toString(); const auto *webbrowserWindow = qobject_cast(engine.rootObjects().constFirst()); if (!webbrowserWindow) { qWarning() << "No webbrowser window is open, can't open the url"; return; } const auto *pageStack = webbrowserWindow->property("pageStack").value(); const auto *initialPage = pageStack->property("initialPage").value(); // This should be initialPage->findChild(QStringLiteral("regularTabsObject")), for some reason // it doesn't find our tabsModel. const auto children = initialPage->children(); const auto *regularTabs = *std::find_if(children.cbegin(), children.cend(), [](const QObject *child) { return child->objectName() == QStringLiteral("regularTabsObject"); }); auto *tabsModel = regularTabs->property("tabsModel").value(); // Open new tab with requested url tabsModel->newTab(initialUrl); // Move window to the front KWindowSystem::raiseWindow(webbrowserWindow->winId()); } }); // register as early possible so it has time to load, constructor doesn't block qmlRegisterSingletonInstance(APPLICATION_ID, 1, 0, "AdblockUrlInterceptor", &AdblockUrlInterceptor::instance()); engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.addImageProvider(IconImageProvider::providerId(), new IconImageProvider(&engine)); // initial url command line parameter if (!parser.positionalArguments().isEmpty()) { const QString initialUrl = QUrl::fromUserInput(parser.positionalArguments().constFirst()).toString(); BrowserManager::instance()->setInitialUrl(initialUrl); } // Exported types qmlRegisterType(APPLICATION_ID, 1, 0, "BookmarksHistoryModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "UrlObserver"); qmlRegisterType(APPLICATION_ID, 1, 0, "UserAgentGenerator"); qmlRegisterType(APPLICATION_ID, 1, 0, "TabsModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "AngelfishWebProfile"); qmlRegisterSingletonInstance(APPLICATION_ID, 1, 0, "Settings", AngelfishSettings::self()); qmlRegisterType(APPLICATION_ID, 1, 0, "AdblockFilterListsModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "DownloadsModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "WebAppManagerModel"); qmlRegisterType(APPLICATION_ID, 1, 0, "WebAppCreator"); qmlRegisterAnonymousType(APPLICATION_ID, 1); // URL utils qmlRegisterSingletonType(APPLICATION_ID, 1, 0, "UrlUtils", [](QQmlEngine *, QJSEngine *) -> QObject * { return new UrlUtils(); }); // Browser Manager qmlRegisterSingletonType(APPLICATION_ID, 1, 0, "BrowserManager", [](QQmlEngine *, QJSEngine *) -> QObject * { return BrowserManager::instance(); }); Q_INIT_RESOURCE(resources); QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, QApplication::instance(), [] { AngelfishSettings::self()->save(); }); // Setup Unix signal handlers const auto unixExitHandler = [](int /*sig*/) -> void { QCoreApplication::quit(); }; const std::array quitSignals = {SIGQUIT, SIGINT, SIGTERM, SIGHUP}; sigset_t blockingMask; sigemptyset(&blockingMask); for (const auto sig : quitSignals) { sigaddset(&blockingMask, sig); } struct sigaction sa; sa.sa_handler = unixExitHandler; sa.sa_mask = blockingMask; sa.sa_flags = 0; for (auto sig : quitSignals) { sigaction(sig, &sa, nullptr); } // Load QML engine.load(QUrl(QStringLiteral("qrc:///webbrowser.qml"))); const auto *window = qobject_cast(engine.rootObjects().constFirst()); QObject::connect(window, &QQuickWindow::widthChanged, AngelfishSettings::self(), [window] { AngelfishSettings::setWindowWidth(window->width()); }); QObject::connect(window, &QQuickWindow::heightChanged, AngelfishSettings::self(), [window] { AngelfishSettings::setWindowHeight(window->height()); }); QObject::connect(window, &QQuickWindow::xChanged, AngelfishSettings::self(), [window] { AngelfishSettings::setWindowX(window->x()); }); QObject::connect(window, &QQuickWindow::yChanged, AngelfishSettings::self(), [window] { AngelfishSettings::setWindowY(window->y()); }); // Error handling if (engine.rootObjects().isEmpty()) { return -1; } return app.exec(); } angelfish-v21.07/src/regex-weburl/000077500000000000000000000000001407527632300170615ustar00rootroot00000000000000angelfish-v21.07/src/regex-weburl/regex-weburl.js000066400000000000000000000067601407527632300220400ustar00rootroot00000000000000// // Regular Expression for URL validation // // Author: Diego Perini // Created: 2010/12/05 // Updated: 2018/09/12 // Modified: 2019/01/05 (Simon Schmeisser) // License: MIT // // SPDX-FileCopyrightText: 2010-2018 Diego Perini (http://www.iport.it) // // SPDX-License-Identifier: MIT // // the regular expression composed & commented // could be easily tweaked for RFC compliance, // it was expressly modified to fit & satisfy // these test for an URL shortener: // // http://mathiasbynens.be/demo/url-regex // // Notes on possible differences from a standard/generic validation: // // - utf-8 char class take in consideration the full Unicode range // - TLDs have been made mandatory so single names like "localhost" fails // - protocols have been restricted to ftp, http and https only as requested // // Changes: // // - IP address dotted notation validation, range: 1.0.0.0 - 223.255.255.255 // first and last IP address of each class is considered invalid // (since they are broadcast/network addresses) // // - Made starting path slash optional (http://example.com?foo=bar) // - Allow a dot (.) at the end of hostnames (http://example.com.) // - Allow an underscore (_) character in host/domain names // - Check dot delimited parts length and total length // - Made protocol optional, allowed short syntax // // // local changes: // // - removed "short syntax //", either http://, https://, ftp:// or just the domain // // Compressed one-line versions: // // Javascript regex version // // /^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i // // PHP version (uses % symbol as delimiter) // // %^(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\x{00a1}-\x{ffff}][a-z0-9\x{00a1}-\x{ffff}_-]{0,62})?[a-z0-9\x{00a1}-\x{ffff}]\.)+(?:[a-z\x{00a1}-\x{ffff}]{2,})\.?))(?::\d{2,5})?(?:[/?#]\S*)?$%iuS // var re_weburl = new RegExp( "^" + // protocol identifier (optional) "(?:(?:https?|ftp):\\/\\/)?" + // user:pass BasicAuth (optional) "(?:\\S+(?::\\S*)?@)?" + "(?:" + // IP address dotted notation octets // excludes loopback network 0.0.0.0 // excludes reserved space >= 224.0.0.0 // excludes network & broacast addresses // (first & last IP address of each class) "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" + // host & domain names, may end with dot // can be replaced by a shortest alternative // (?![-_])(?:[-\\w\\u00a1-\\uffff]{0,63}[^-_]\\.)+ "(?:" + "(?:" + "[a-z0-9\\u00a1-\\uffff]" + "[a-z0-9\\u00a1-\\uffff_-]{0,62}" + ")?" + "[a-z0-9\\u00a1-\\uffff]\\." + ")+" + // TLD identifier name, may end with dot "(?:[a-z\\u00a1-\\uffff]{2,}\\.?)" + ")" + // port number (optional) "(?::\\d{2,5})?" + // resource path (optional) "(?:[/?#]\\S*)?" + "$", "i" ); angelfish-v21.07/src/rs/000077500000000000000000000000001407527632300150755ustar00rootroot00000000000000angelfish-v21.07/src/rs/adblock/000077500000000000000000000000001407527632300164745ustar00rootroot00000000000000angelfish-v21.07/src/rs/adblock/Cargo.toml000066400000000000000000000010661407527632300204270ustar00rootroot00000000000000# SPDX-FileCopyrightText: 2020 Jonah Brüchert # # SPDX-License-Identifier: GPL-2.0-or-later [package] name = "angelfish-adblock" version = "0.1.0" authors = ["Jonah Brüchert "] edition = "2018" [lib] crate-type = ["staticlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] cxx-build = "1.0" [dependencies] adblock = {version = "0.3", default-features = false, features = [ "full-regex-handling", "object-pooling", "embedded-domain-resolver" ]} cxx = "1.0" angelfish-v21.07/src/rs/adblock/build.rs000066400000000000000000000003351407527632300201420ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later extern crate cxx_build; fn main() { cxx_build::bridge("src/adblock.rs").compile("angelfish-adblock") } angelfish-v21.07/src/rs/adblock/src/000077500000000000000000000000001407527632300172635ustar00rootroot00000000000000angelfish-v21.07/src/rs/adblock/src/adblock.rs000066400000000000000000000116731407527632300212400ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later use std::fs; use std::io::Write; use adblock::engine::Engine; use adblock::lists::{FilterFormat, FilterSet}; use crate::adblock_debug; struct Adblock { blocker: Option, needs_save: bool, } /// creates a new adblock object, and returns a pointer to it. /// If the passed list_dir is invalid, the Adblock will not contain an engine. fn new_adblock(list_dir: &str) -> Box { adblock_debug!("## Creating new adblock instance"); // Create domain resolver let mut filter_set = FilterSet::new(true); // iterate directory if let Ok(entries) = fs::read_dir(list_dir) { for entry in entries.into_iter().flatten() { if let Ok(ft) = entry.file_type() { if ft.is_file() { adblock_debug!("Loading filter {:?}", entry); match fs::read_to_string(&entry.path()) { Ok(contents) => { filter_set.add_filter_list(&contents, FilterFormat::Standard); } Err(e) => { adblock_debug!("Loading filter {:?} failed: {}", entry.path(), e); } } } } } let blocker = Engine::from_filter_set(filter_set, true); return Box::new(Adblock { blocker: Some(blocker), needs_save: true, }); } Box::new(Adblock { blocker: None, needs_save: false, }) } fn load_adblock(path: &str) -> Box { let engine = match std::fs::read(path) { Ok(data) => { let mut engine = Engine::new(true); match engine.deserialize(&data) { Ok(()) => Some(engine), Err(e) => { adblock_debug!("Failed to deserialize saved adblock data: {:?}", e); None } } } Err(e) => { adblock_debug!("Failed to read adblock state from disk: {:?}", e); None } }; Box::from(Adblock { blocker: engine, needs_save: false, }) } impl Adblock { /// returns a boxed AdblockResult object with information on whether /// the request should be blocked or redirected. fn should_block(&self, url: &str, source_url: &str, request_type: &str) -> ffi::AdblockResult { if let Some(engine) = &self.blocker { let blocker_result = engine.check_network_urls(url, source_url, request_type); adblock_debug!("Blocker input: {}, {}, {}", url, source_url, request_type); adblock_debug!("Blocker result: {:?}", blocker_result); return ffi::AdblockResult { matched: blocker_result.matched, important: blocker_result.important, redirect: blocker_result.redirect.unwrap_or_default(), }; } else { adblock_debug!("Adblock engine doesn't exist! Probably it failed to load or restore"); } ffi::AdblockResult::default() } fn save(&self, path: &str) -> bool { match fs::File::create(path) { Ok(mut file) => match &self.blocker { Some(engine) => match engine.serialize_raw() { Ok(data) => match file.write_all(&data) { Ok(()) => { adblock_debug!("Successfully saved adblock cache"); return true; } Err(e) => adblock_debug!("Failed to write adblock cache: {:?}", e), }, Err(e) => adblock_debug!("Can't save adblock cache: {:?}", e), }, None => adblock_debug!("Can't save adblock cache, since the engine is not loaded."), }, Err(e) => adblock_debug!("Can't save adblock cache, failed to write to file: {:?}", e), } false } fn is_valid(&self) -> bool { self.blocker.is_some() } fn needs_save(&self) -> bool { self.needs_save } } #[cxx::bridge] mod ffi { #[derive(Default)] struct AdblockResult { matched: bool, important: bool, redirect: String, } extern "Rust" { type Adblock; #[cxx_name="newAdblock"] fn new_adblock(list_dir: &str) -> Box; #[cxx_name="loadAdblock"] fn load_adblock(path: &str) -> Box; #[cxx_name="isValid"] fn is_valid(self: &Adblock) -> bool; #[cxx_name="needsSave"] fn needs_save(self: &Adblock) -> bool; #[cxx_name="shouldBlock"] fn should_block( self: &Adblock, url: &str, source_url: &str, request_type: &str, ) -> AdblockResult; fn save(self: &Adblock, path: &str) -> bool; } } angelfish-v21.07/src/rs/adblock/src/lib.rs000066400000000000000000000002131407527632300203730ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later mod adblock; mod logging; angelfish-v21.07/src/rs/adblock/src/logging.rs000066400000000000000000000013061407527632300212570ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later use std::ffi::CString; use std::os::raw::c_char; use std::mem::drop; extern "C" { fn q_cdebug_adblock(message: *const c_char); } pub fn adblock_debug(message: &str) { if let Ok(cstring) = CString::new(message) { unsafe { let ptr = cstring.into_raw(); q_cdebug_adblock(ptr); drop(CString::from_raw(ptr)); }; } } #[macro_export] macro_rules! adblock_debug { ($arg:literal) => ({ $crate::logging::adblock_debug($arg); }); ($($arg:tt)*) => ({ $crate::logging::adblock_debug(&format!($($arg)*)); }); } angelfish-v21.07/src/webappcreator.cpp000066400000000000000000000040301407527632300200100ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020-2021 Jonah Brüchert // // SPDX-License-Identifier: LGPL-2.0-or-later #include "webappcreator.h" #include #include #include #include #include #include #include #include #include #include "webappmanager.h" WebAppCreator::WebAppCreator(QObject *parent) : QObject(parent) , m_webAppMngr(WebAppManager::instance()) { connect(this, &WebAppCreator::websiteNameChanged, this, &WebAppCreator::existsChanged); connect(&m_webAppMngr, &WebAppManager::applicationsChanged, this, &WebAppCreator::existsChanged); } bool WebAppCreator::exists() const { return m_webAppMngr.exists(m_websiteName); } const QString &WebAppCreator::websiteName() const { return m_websiteName; } void WebAppCreator::setWebsiteName(const QString &websiteName) { m_websiteName = websiteName; Q_EMIT websiteNameChanged(); } void WebAppCreator::createDesktopFile(const QString &name, const QString &url, const QString &iconUrl) { m_webAppMngr.addApp(name, url, fetchIcon(iconUrl)); // Refresh homescreen entries on Plasma Mobile QProcess buildsycoca; buildsycoca.setProgram(QStringLiteral("kbuildsycoca5")); buildsycoca.startDetached(); } QImage WebAppCreator::fetchIcon(const QString &url) { auto *provider = static_cast(qmlEngine(this)->imageProvider(QStringLiteral("favicon"))); const QStringView prefixFavicon = QStringView(u"image://favicon/"); const QString providerIconName = url.mid(prefixFavicon.size()); const QSize szRequested; switch (provider->imageType()) { case QQmlImageProviderBase::Image: { return provider->requestImage(providerIconName, nullptr, szRequested); } case QQmlImageProviderBase::Pixmap: { return provider->requestPixmap(providerIconName, nullptr, szRequested).toImage(); } default: qDebug() << "Failed to save unhandled image type"; } return QImage(); } angelfish-v21.07/src/webappcreator.h000066400000000000000000000015561407527632300174670ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2020 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include class QQmlEngine; class WebAppManager; class WebAppCreator : public QObject { Q_OBJECT Q_PROPERTY(QString websiteName READ websiteName WRITE setWebsiteName NOTIFY websiteNameChanged) Q_PROPERTY(bool exists READ exists NOTIFY existsChanged) public: explicit WebAppCreator(QObject *parent = nullptr); const QString &websiteName() const; void setWebsiteName(const QString &websiteName); Q_SIGNAL void websiteNameChanged(); bool exists() const; Q_SIGNAL void existsChanged(); Q_INVOKABLE void createDesktopFile(const QString &name, const QString &url, const QString &icon); private: QString m_websiteName; QImage fetchIcon(const QString &url); WebAppManager &m_webAppMngr; }; angelfish-v21.07/src/webappmanager.cpp000066400000000000000000000105561407527632300177750ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "webappmanager.h" #include #include #include #include #include WebAppManager::WebAppManager(QObject *parent) : QObject(parent) , m_desktopFileDirectory(desktopFileDirectory()) { const auto fileInfos = m_desktopFileDirectory.entryInfoList(QDir::Files); // Likely almost all files in the directory are webapps, so this should be worth it m_webApps.reserve(fileInfos.size()); for (const auto &file : fileInfos) { // Make sure to only parse desktop files if (file.fileName().contains(QStringView(u".desktop"))) { KDesktopFile desktopFile(file.filePath()); // Only handle desktop files referencing angelfish-webapp if (desktopFile.group("Desktop Entry").readEntry("Exec").contains(QStringView(u"angelfish-webapp"))) { WebApp app{desktopFile.readName(), desktopFile.readIcon(), desktopFile.readUrl()}; m_webApps.push_back(std::move(app)); } } } } QString WebAppManager::desktopFileDirectory() { if (isFlatpak()) { return qEnvironmentVariable("HOME") % u"/.local/share/applications/"; } return QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); } QString WebAppManager::iconDirectory() { if (isFlatpak()) { return qEnvironmentVariable("HOME") % u"/.local/share/icons/hicolor/16x16/apps/"; } return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/icons/hicolor/16x16/apps/"); } const std::vector &WebAppManager::applications() const { return m_webApps; } void WebAppManager::addApp(const QString &name, const QString &url, const QImage &icon) { const QString filename = generateFileName(name); const QString desktopFileName = generateDesktopFileName(name); icon.save(iconDirectory() % QDir::separator() % filename % u".png", "PNG"); KConfig desktopFile(desktopFileDirectory() % QDir::separator() % desktopFileName, KConfig::SimpleConfig); auto desktopEntry = desktopFile.group("Desktop Entry"); desktopEntry.writeEntry(QStringLiteral("URL"), url); desktopEntry.writeEntry(QStringLiteral("Name"), name); desktopEntry.writeEntry(QStringLiteral("Exec"), QString(webAppCommand() % u' ' % desktopFileName)); desktopEntry.writeEntry(QStringLiteral("Icon"), filename); m_webApps.push_back(WebApp{name, filename, url}); desktopFile.sync(); Q_EMIT applicationsChanged(); } bool WebAppManager::exists(const QString &name) { const QString location = desktopFileDirectory(); const QString filename = generateDesktopFileName(name); return QFile::exists(location % QDir::separator() % filename); } bool WebAppManager::removeApp(const QString &name) { const QString location = desktopFileDirectory(); const QString filename = generateDesktopFileName(name); auto it = std::remove_if(m_webApps.begin(), m_webApps.end(), [&name](const WebApp &app) { return app.name == name; }); m_webApps.erase(it); bool success = QFile::remove(location % QDir::separator() % filename); Q_EMIT applicationsChanged(); return success; } WebAppManager &WebAppManager::instance() { static WebAppManager instance; return instance; } QString WebAppManager::generateFileName(const QString &name) { QString filename = name.toLower(); filename.replace(QChar(u' '), QChar(u'_')); filename.remove(u'/'); filename.remove(u'"'); filename.remove(u'\''); filename.remove(u','); filename.remove(u'.'); filename.remove(u'|'); return filename; } QString WebAppManager::generateDesktopFileName(const QString &name) { return generateFileName(name) % u".desktop"; } QString WebAppManager::webAppCommand() { if (isFlatpak()) { return QStringLiteral( "flatpak run " "--command=angelfish-webapp " "--filesystem=%1 " "org.kde.angelfish") .arg(desktopFileDirectory()); } return QStringLiteral("angelfish-webapp"); } bool WebAppManager::isFlatpak() { static bool isFlatpak = !QStandardPaths::locate(QStandardPaths::RuntimeLocation, QStringLiteral("flatpak-info")).isEmpty(); return isFlatpak; } angelfish-v21.07/src/webappmanager.h000066400000000000000000000020001407527632300174230ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include struct WebApp { QString name; QString icon; QString url; }; class WebAppManager : public QObject { Q_OBJECT public: explicit WebAppManager(QObject *parent = nullptr); static QString desktopFileDirectory(); static QString iconDirectory(); const std::vector &applications() const; void addApp(const QString &name, const QString &url, const QImage &icon); bool exists(const QString &name); bool removeApp(const QString &name); static WebAppManager &instance(); Q_SIGNALS: void applicationsChanged(); private: static bool isFlatpak(); static QString generateFileName(const QString &name); static QString generateDesktopFileName(const QString &name); static QString webAppCommand(); private: QDir m_desktopFileDirectory; std::vector m_webApps; }; angelfish-v21.07/src/webappmanagermodel.cpp000066400000000000000000000025661407527632300210200ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #include "webappmanagermodel.h" #include "webappmanager.h" WebAppManagerModel::WebAppManagerModel(QObject *parent) : QAbstractListModel(parent) , m_webAppMngr(WebAppManager::instance()) { } WebAppManagerModel::~WebAppManagerModel() = default; int WebAppManagerModel::rowCount(const QModelIndex &index) const { return index.isValid() ? 0 : int(m_webAppMngr.applications().size()); } QVariant WebAppManagerModel::data(const QModelIndex &index, int role) const { switch (role) { case Role::NameRole: return m_webAppMngr.applications()[index.row()].name; case Role::IconRole: return WebAppManager::iconDirectory() + QDir::separator() + m_webAppMngr.applications()[index.row()].icon; case Role::UrlRole: return m_webAppMngr.applications()[index.row()].url; } Q_UNREACHABLE(); return {}; } QHash WebAppManagerModel::roleNames() const { return { {Role::NameRole, QByteArrayLiteral("name")}, {Role::IconRole, QByteArrayLiteral("desktopIcon")}, {Role::UrlRole, QByteArrayLiteral("url")}, }; } void WebAppManagerModel::removeApp(int index) { beginRemoveRows({}, index, index); m_webAppMngr.removeApp(m_webAppMngr.applications()[index].name); endRemoveRows(); } angelfish-v21.07/src/webappmanagermodel.h000066400000000000000000000013131407527632300204520ustar00rootroot00000000000000// SPDX-FileCopyrightText: 2021 Jonah Brüchert // // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include class WebAppManager; class WebAppManagerModel : public QAbstractListModel { Q_OBJECT enum Role { NameRole, IconRole, UrlRole, }; public: explicit WebAppManagerModel(QObject *parent = nullptr); ~WebAppManagerModel(); int rowCount(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role) const override; QHash roleNames() const override; Q_INVOKABLE void removeApp(int index); private: WebAppManager &m_webAppMngr; };