├── .editorconfig
├── .github
└── FUNDING.yml
├── .gitignore
├── .travis.yml
├── CODE OF CONDUCT.md
├── CONTRIBUTING.md
├── COPYING
├── README.md
├── arch
├── .SRCINFO
└── PKGBUILD
├── bin
├── README
├── edit-messages.py
├── errorserver.py
├── extract-message.py
├── merge-messages.py
├── message-stats.py
├── parsing.py
└── weblate-format.py-draft
├── com.github.alcinnz.odysseus.json
├── data
├── Screenshot-download-find.png
├── Screenshot-feed.png
├── Screenshot-history.png
├── Screenshot-home.png
├── com.github.alcinnz.odysseus.appdata.xml.in
├── com.github.alcinnz.odysseus.desktop.in
├── com.github.alcinnz.odysseus.json
├── db
│ ├── STYLEGUIDE
│ ├── database.gresource.xml
│ └── init.sql
├── icons
│ ├── 16
│ │ └── com.github.alcinnz.odysseus.svg
│ ├── 24
│ │ └── com.github.alcinnz.odysseus.svg
│ ├── 32
│ │ └── com.github.alcinnz.odysseus.svg
│ ├── 48
│ │ └── com.github.alcinnz.odysseus.svg
│ ├── 64
│ │ └── com.github.alcinnz.odysseus.svg
│ └── 128
│ │ └── com.github.alcinnz.odysseus.svg
├── meson.build
├── page-l10n
│ ├── Odysseus.messages
│ ├── README
│ ├── fr
│ ├── l10n.gresource.xml
│ └── nl
└── pages
│ ├── INVALID-FORM
│ ├── NOT-FOUND
│ ├── README.md
│ ├── SERVER-ERROR
│ ├── about-pages.gresource.xml
│ ├── bookmarks
│ ├── butterick.css
│ ├── com.github.bleakgrey.liberate-symbolic.svg
│ ├── credits
│ ├── debugging
│ ├── include-test
│ ├── index
│ ├── syntaxerror0
│ └── test
│ ├── errors
│ ├── 200
│ ├── 201
│ ├── 202
│ ├── 203
│ ├── 204
│ ├── 206
│ ├── 300
│ ├── 301
│ ├── 302
│ ├── 304
│ ├── 305
│ ├── 400
│ ├── 401
│ ├── 402
│ ├── 403
│ ├── 404
│ ├── 405
│ ├── 406
│ ├── 407
│ ├── 408
│ ├── 409
│ ├── 410
│ ├── 412
│ ├── 416
│ ├── 417
│ ├── 418
│ ├── 422
│ ├── 423
│ ├── 424
│ ├── 426
│ ├── 429
│ ├── 451
│ ├── 500
│ ├── 501
│ ├── 503
│ ├── 506
│ ├── 507
│ ├── 511
│ ├── 205.link
│ ├── 303.link
│ ├── 307.link
│ ├── 308.link
│ ├── 411.link
│ ├── 413.link
│ ├── 414.link
│ ├── 415.link
│ ├── 421.link
│ ├── 428.link
│ ├── 431.link
│ ├── 502.link
│ ├── 504.link
│ ├── 505.link
│ ├── 507.icon
│ ├── 508.link
│ ├── 510.link
│ ├── README.md
│ ├── bad-certificate
│ ├── crashed
│ ├── dns
│ ├── network
│ ├── no-source
│ ├── protocol
│ ├── schema
│ ├── uri
│ └── xxx
│ ├── ext
│ ├── README.md
│ ├── highlight.pack.js
│ ├── highlight.pack.js.mime
│ ├── hljs-solarized-light.css
│ ├── hljs-solarized-light.css.mime
│ ├── html5sortable.js
│ └── html5sortable.js.mime
│ ├── feedreaders
│ ├── filetype
│ ├── history
│ ├── history-DESIGN
│ ├── home
│ ├── privacy
│ ├── special
│ ├── README
│ ├── applist
│ ├── restore
│ └── viewsource
│ ├── spring-clean
│ ├── suggestions
│ ├── view
│ ├── viewers
│ ├── application
│ │ ├── atom+xml
│ │ ├── rss+xml
│ │ └── xml
│ └── text
│ │ └── xml
│ └── webfeed-subscribe-symbolic.svg
├── debian
├── changelog
├── compat
├── control
├── copyright
├── rules
└── source
│ └── format
├── default.nix
├── make-full.sh
├── meson.build
├── meson_options.txt
├── nix
├── .gitignore
├── default.nix
├── nixpkgs
│ ├── .gitignore
│ ├── bump.sh
│ ├── default.nix
│ ├── url.json
│ └── url.nix
├── odysseus.nix
└── patches
│ └── hxwls-path.patch
├── po
├── LINGUAS
├── POTFILES
├── com.github.alcinnz.odysseus.pot
├── cs.po
├── extra
│ ├── LINGUAS
│ ├── POTFILES
│ ├── cs.po
│ ├── extra.pot
│ ├── fr.po
│ ├── meson.build
│ ├── nl.po
│ └── pt.po
├── fr.po
├── meson.build
├── nl.po
└── pt_BR.po
├── src
├── BrowserWindow.vala
├── Models
│ ├── Download.vala
│ ├── DownloadSet.vala
│ ├── I18nUtil.vala
│ ├── ImageUtil.vala
│ ├── Links.vala
│ └── StatusIndicator.vala
├── Odysseus.vala
├── Persistance.vala
├── Services
│ ├── Prosody
│ │ ├── data.vala
│ │ ├── expression.vala
│ │ ├── lib.vala
│ │ ├── loader.vala
│ │ ├── misc
│ │ │ ├── AppStream.vala
│ │ │ ├── diff.vala
│ │ │ ├── favicons.vala
│ │ │ ├── http.vala
│ │ │ ├── i18n.vala
│ │ │ ├── json.vala
│ │ │ ├── mimeinfo.vala
│ │ │ ├── test.vala
│ │ │ ├── tsv.vala
│ │ │ └── xml.vala
│ │ ├── parser.vala
│ │ ├── slice.vala
│ │ └── writers.vala
│ ├── database
│ │ ├── completer.vala
│ │ ├── database.vala
│ │ ├── prosody.vala
│ │ ├── psuedorest.vala
│ │ ├── tagging.vala
│ │ └── util.vala
│ ├── globals.vala
│ ├── icons.vala
│ └── pages.vala
├── Traits
│ ├── README
│ ├── autocomplete
│ │ ├── bookmarks.vala
│ │ ├── ddg-autocomplete.vala
│ │ ├── duckduckgo.vala
│ │ ├── history.vala
│ │ ├── imply-http.vala
│ │ ├── redpanda.vala
│ │ └── sample-completion.json
│ ├── decorate
│ │ ├── AutoScroll.vala
│ │ ├── addressbar-autofocus.vala
│ │ ├── alert.vala
│ │ ├── fix-bg-videos.vala
│ │ ├── fix-plus.google.com.vala
│ │ ├── internal-favicons.vala
│ │ ├── notifications.vala
│ │ ├── permit.vala
│ │ └── settings.vala
│ ├── download-progress.vala
│ ├── download-window.vala
│ ├── init.vala
│ ├── navigate
│ │ ├── autodownload.vala
│ │ ├── errors.vala
│ │ ├── harvest-recommendations.vala
│ │ ├── history.vala
│ │ ├── newtab.vala
│ │ ├── other-types.vala
│ │ ├── persist-tab-history.vala
│ │ ├── screenshot-proxy.vala
│ │ ├── spring-clean.vala
│ │ └── viewsource.vala
│ └── status
│ │ ├── https.vala
│ │ ├── liberate.vala
│ │ ├── local.vala
│ │ └── webfeed.vala
├── Widgets
│ ├── Chromeless.vala
│ ├── DownloadBar.vala
│ ├── DownloadButton.vala
│ ├── ProgressBin.vala
│ ├── VerticalTabs.vala
│ ├── WebNotebook.vala
│ ├── WebTab.vala
│ ├── header
│ │ ├── AddressBar2.vala
│ │ ├── ButtonWithMenu.vala
│ │ └── HeaderBarWithMenus.vala
│ └── overlays
│ │ ├── Bookmarker.vala
│ │ ├── FindToolbar.vala
│ │ └── InfoContainer.vala
├── ext
│ ├── README.md
│ ├── liberate
│ │ ├── Liberate.vala
│ │ ├── Reader.vala
│ │ ├── data
│ │ │ ├── Readability-readerable.js
│ │ │ ├── Readability.js
│ │ │ ├── gresource.xml
│ │ │ ├── is_readable.js
│ │ │ ├── patcher.js
│ │ │ ├── theme-light.css
│ │ │ ├── theme-moonlight.css
│ │ │ └── theme-solarized.css
│ │ ├── liberate.deps
│ │ └── meson.build
│ └── tokenized-entry
│ │ ├── AutomaticScrollBox.vala
│ │ ├── Completer.vala
│ │ └── tokenized.vala
└── meson.build
├── ui.sqlite.sample
└── viewer
├── oddysseus-viewer
└── oddysseus-viewer.vala
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig
2 | root = true
3 |
4 | # elementary defaults
5 | [*]
6 | charset = utf-8
7 | end_of_line = lf
8 | indent_size = tab
9 | indent_style = space
10 | insert_final_newline = true
11 | max_line_length = 80
12 | tab_width = 4
13 |
14 | # Markup files
15 | [{*.html,*.xml,*.xml.in,*.yml}]
16 | tab_width = 2
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom: https://liberapay.com/alcinnz
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | *~
3 | /result
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | language: node_js
4 |
5 | node_js:
6 | - 10.17.0
7 |
8 | sudo: required
9 |
10 | services:
11 | - docker
12 |
13 | addons:
14 | apt:
15 | sources:
16 | - ubuntu-toolchain-r-test
17 | packages:
18 | - libstdc++-5-dev
19 |
20 | install:
21 | - npm i -g @elementaryos/houston
22 |
23 | script:
24 | - houston ci
25 |
--------------------------------------------------------------------------------
/CODE OF CONDUCT.md:
--------------------------------------------------------------------------------
1 | Odysseus is considered to fall under [elementary's code of conduct](https://elementary.io/code-of-conduct). Please keep conversations respectful and ontopic.
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Odysseus
2 | So you have a feature you want integrated into Odysseus and are willing to put the work into, or you simply want to help out? Then this guide will help you.
3 |
4 | To start join the [Matrix chatroom](https://riot.im/app/#/room/#odysseus-web:matrix.org), clone https://github.com/alcinnz/Odysseus.git, and optionally fork the repository on GitHub. If you do not want to sign up for GitHub you are welcome to contribute entirely via Matrix.
5 |
6 | If you need to discuss anything or want to submit an issue, feel free to do so via either the Matrix chatroom or the [issue tracker](https://github.com/alcinnz/Odysseus/issues).
7 |
8 | Once you feel you're code is ready to contribute upstream (and there's no failures in [the Prosody tests](odysseus:debugging/test)) open a GitHub pull request or run [`git format-patch`](https://git-scm.com/docs/git-format-patch) and submit it to the Matrix chatroom.
9 |
10 | ## Governance
11 | Currently Odysseus is governed by a BDFL, Adrian Cochrane. Though that may adjusted as the project gets larger.
12 |
13 | While he'll welcome any and all features into Odysseus, all contributions will be reviewed not just on a code quality basis but a usability and privacy. But as long as you keep the following concerns in mind your contribution is likely to be accepted:
14 |
15 | * Is your new feature more useful than the complexity (in terms of both UI and performance) it adds?
16 | * Are you sending information over the Internet? Have you gained consent? Can you minimize how much can be inferred from it?
17 | * Are relying on any particular website? Please minimize that.
18 | * Can you allow more than a single site/page to be integrated into Odysseus via your feature?
19 | * Which open standards are relevant to it?
20 |
21 | ## Code Style
22 |
23 | * Each file should contain comments declaring it to be under the GPLv3+, who has contributed to it and in which years, and a description of what that file does and why it's there.
24 | * If your code can be implemented in Prosody, and/or as self-contained "traits" ontop of WebKitGTK and Odysseus's core UI, do so.
25 | * Use the GNOME coding styles with 4 space indents and no spaces between method names and their parenthesese.
26 |
27 | ---
28 |
29 | See how this documentation can be improved? Then please contribute to it!
30 |
--------------------------------------------------------------------------------
/arch/.SRCINFO:
--------------------------------------------------------------------------------
1 | pkgbase = odysseus
2 | pkgdesc = Web browser for the open and decentralized web.
3 | pkgver = 1.5.18
4 | pkgrel = 1
5 | url = https://odysseus.adrian.geek.nz
6 | arch = i686
7 | arch = x86_64
8 | license = GPL
9 | makedepends = meson
10 | makedepends = vala
11 | depends = gtk3
12 | depends = granite
13 | depends = webkit2gtk
14 | depends = json-glib
15 | depends = libsoup
16 | depends = sqlite
17 | depends = appstream
18 | depends = gcr
19 | depends = gettext
20 | optdepends = archlinux-appstream-data: compatible app recommendations for hyperlinks
21 | optdepends = html-xml-utils: webfeed autodiscovery
22 | source = https://github.com/alcinnz/Odysseus/archive/1.5.18.tar.gz
23 | md5sums = 939f5f1811359a00146a6e0c7105f547
24 |
25 | pkgname = odysseus
26 |
27 |
--------------------------------------------------------------------------------
/arch/PKGBUILD:
--------------------------------------------------------------------------------
1 | # Maintainer: Adrian Cochrane
2 | pkgname=odysseus
3 | pkgver=1.6.1
4 | pkgrel=1
5 | pkgdesc="Web browser for the open and decentralized web."
6 | url="https://odysseus.adrian.geek.nz"
7 | license=('GPL')
8 | arch=('i686' 'x86_64')
9 | depends=(
10 | 'gtk3'
11 | 'granite'
12 | 'webkit2gtk'
13 | 'json-glib'
14 | 'libsoup'
15 | 'sqlite'
16 | 'appstream'
17 | 'gcr'
18 | 'gettext'
19 | )
20 | makedepends=(
21 | 'meson'
22 | 'vala'
23 | )
24 | optdepends=(
25 | 'archlinux-appstream-data: compatible app recommendations for hyperlinks'
26 | 'html-xml-utils: webfeed autodiscovery'
27 | )
28 | source=(https://github.com/alcinnz/Odysseus/archive/$pkgver.tar.gz)
29 | md5sums=(5118b35f491da1b7ad19b49565f6f15d) #autofill using updpkgsums
30 |
31 | build() {
32 | cd "Odysseus-$pkgver"
33 |
34 | [ -d build ] && rm -rf build
35 | meson build --prefix=/usr --buildtype=release
36 | ninja -C build
37 | }
38 |
39 | package() {
40 | cd "Odysseus-$pkgver"
41 |
42 | DESTDIR="$pkgdir" ninja -C build install
43 | }
44 |
--------------------------------------------------------------------------------
/bin/README:
--------------------------------------------------------------------------------
1 | Scripts to aid development.
2 |
--------------------------------------------------------------------------------
/bin/edit-messages.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python3
2 | """
3 | Guides localizers in how to edit the translation files.
4 | """
5 | from parsing import parse_messages, tag_re
6 | from sys import argv
7 | from os.path import isdir
8 |
9 | basepath = "data/page-l10n/" if isdir(".git") else "./"
10 |
11 | def indent(text):
12 | return "\t" + "\n\t".join(text.split("\n"))
13 |
14 | def input_lines(prompt=""):
15 | print(prompt, "[Enter a blank line to end]")
16 | lines = []
17 | line = input()
18 | while line:
19 | lines.append(line)
20 | line = input()
21 | return "\n".join(lines).strip()
22 |
23 | def prompt_locale():
24 | from os import listdir
25 | print(
26 | "Please select a locale to translate",
27 | listdir(basepath) if isdir(".git") else "",
28 | end=":\t"
29 | )
30 | return input()
31 |
32 | filename = argv[1] if len(argv) > 1 else prompt_locale()
33 | show_all = len(argv) > 2 and argv[2] == "all"
34 | if not show_all:
35 | print("To review all translations, pass the desired ISO language code and the text 'all' as commandline arguments.")
36 |
37 | catalogue = parse_messages(basepath + filename)
38 |
39 | translations = []
40 | print("To exit, press Ctrl-C")
41 | for english, open_tag, translation in catalogue:
42 | if show_all or not translation:
43 | print(indent(english))
44 |
45 | if translation:
46 | print("Currently translated to:")
47 | print(indent(translation))
48 | print("Enter a blank line to keep")
49 |
50 | tags = [tag for tag in tag_re.split(english) if tag and tag[0] == "{"]
51 | if tags:
52 | print("Substitution instructions:")
53 | for tag in tags:
54 | print(indent(tag))
55 | if tag[1] == "#": print("\t\t* Note to you, feel free to drop it")
56 | print("Please copy these into your translation verbatim")
57 |
58 | if "<" in english and ">" in english:
59 | print("Text between '<' and '>' are formatting instructions.")
60 | print("If you don't know web development, please keep them as-is.")
61 | if "title=" in english:
62 | print("Though please do translate the quoted text after 'title='.")
63 |
64 | new_translation = input_lines()
65 | if new_translation: translation = new_translation
66 |
67 | translations.append((english, open_tag, translation))
68 | print(translations)
69 |
70 | with open(basepath + filename, "w") as f:
71 | for english, tag, translation in catalogue:
72 | print(tag, file=f)
73 | print(english, file=f)
74 | print("{% trans %}", file=f)
75 | print(translation, file=f)
76 | print("{% endmsg %}", file=f)
77 |
--------------------------------------------------------------------------------
/bin/errorserver.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python3
2 | """Used to test default error pages. """
3 | from http.server import *
4 |
5 | class ErrorServer(BaseHTTPRequestHandler):
6 | def do_GET(self):
7 | self.send_response(int(self.path[1:]))
8 | self.end_headers()
9 |
10 | try:
11 | httpd = HTTPServer(('', 8000), ErrorServer)
12 | httpd.serve_forever()
13 | except KeyboardInterrupt:
14 | httpd.socket.close()
15 |
--------------------------------------------------------------------------------
/bin/extract-message.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | """Parses {% trans %} Prosody tags to extract strings,
3 | which it then writes out into data/page-l10n/Oddysseus and
4 | merges into the other catalogue files in that directory. """
5 | from collections import defaultdict
6 | from parsing import *
7 |
8 | # Utilities
9 | def walk(top):
10 | import os
11 | for path, dirnames, filenames in os.walk(top):
12 | for filename in filenames:
13 | # ignore certain specially interpreted file extensions
14 | if filename.endswith(".mime"): continue
15 | if filename.endswith(".link"): continue
16 | if filename.endswith(".icon"): continue
17 | if filename.endswith("~"): continue
18 | if filename in ("README", "README.md"): continue
19 | if filename.endswith(".gresource.xml"): continue
20 | filepath = os.path.join(path, filename)
21 |
22 | # Final check: does the optional .mime specify it's a template?
23 | try:
24 | with open(filepath + ".mime") as f:
25 | if f.read(1) != "+": continue
26 | except:
27 | "Implicit MIMEType is an HTML template"
28 |
29 | yield filepath, filepath[len(top):]
30 |
31 | # Parsers
32 | def parse_templates(repo_root = "."):
33 | from os import path
34 | for filename, subpath in walk(path.join(repo_root, "data", "pages")):
35 | print("Parsing template", subpath)
36 | template = tags(filename)
37 |
38 | line, text, is_tag = next(template, "trans")
39 | while line:
40 | if is_tag:
41 | message, endtag = block(template, "endtrans")
42 | yield message.strip(), subpath, line
43 | else:
44 | for special in ("{%", "%}", "{{", "}}", "{#", "#}"):
45 | if special in text:
46 | print("Invalid text translation!", repr(text))
47 | exit(1)
48 | yield text.strip(), subpath, line
49 | line, text, is_tag = next(template, "trans")
50 |
51 | def consolidate(messages):
52 | from collections import OrderedDict
53 | ret = OrderedDict()
54 | for msg, tpl, line in messages:
55 | if msg not in ret: ret[msg] = []
56 | ret[msg].append(tpl + ":" + str(line))
57 | return ret.items()
58 |
59 | from sys import argv
60 | import os
61 | repo_root = argv[1] if len(argv) > 1 else "."
62 | l10n_root = os.path.join(repo_root, "data", "page-l10n")
63 |
64 | with open(os.path.join(l10n_root, "Odysseus.messages"), 'w') as out:
65 | for msg, sources in consolidate(parse_templates(repo_root)):
66 | print("{% msg", " ".join(sources), "%}", msg, "{% trans %}{% endmsg %}", file=out)
67 |
68 | print("All messages have been extracted to:",
69 | os.path.join(l10n_root, "Odysseus.messages"))
70 | print("You may now manually merge them with other message files,")
71 | print("we do not yet have any tools to help you with this.")
72 | print("But even if we did, this would require manual review.")
73 |
--------------------------------------------------------------------------------
/bin/merge-messages.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python3
2 | """
3 | Takes messages from a base file and merges in translations from another.
4 | """
5 | from parsing import parse_messages
6 | from sys import argv
7 | from os.path import isdir
8 |
9 | def indent(text):
10 | return "\t" + "\n\t".join(text.split("\n"))
11 |
12 | if len(argv) <= 1:
13 | print("USAGE: bin/merge-messages.py language-file [base-file [output-file]]")
14 | print("If you run this from the root of Odysseus's repository,")
15 | print("the filepaths are relative to data/page-l10n/ .")
16 | print()
17 | print("For language-file, just use your ISO language code.")
18 | print("base-file falls back to 'Odysseus.messages'.")
19 | print("output-file falls back to the same value as base-file.")
20 | exit(1)
21 |
22 | catalogue = {}
23 |
24 | base_path = "data/page-l10n/" if isdir(".git") else "./"
25 | merge_file = base_path + argv[1]
26 | base_file = base_path + (argv[2] if len(argv) > 2 else "Odysseus.messages")
27 | output_file = base_path + argv[3] if len(argv) > 3 else merge_file
28 |
29 | for english, tag, translation in parse_messages(base_file):
30 | catalogue[english] = (tag, translation)
31 |
32 | for english, _, translation in parse_messages(merge_file):
33 | if not translation: continue
34 | if english not in catalogue:
35 | print("Removed translation:")
36 | print(indent(translation))
37 | print("For:")
38 | print(indent(english))
39 | print("Please look for a similar message")
40 |
41 | continue
42 |
43 | tag, old_translation = catalogue[english]
44 | catalogue[english] = (tag, translation)
45 |
46 | if old_translation:
47 | print("Replaced translation:")
48 | print(indent(old_translation))
49 | print("For:")
50 | print(indent(english))
51 | print("With:")
52 | print(indent(translation))
53 |
54 | with open(output_file, "w") as f:
55 | for english, (tag, translation) in catalogue.items():
56 | print(tag, file=f)
57 | print(english, file=f)
58 | print("{% trans %}", file=f)
59 | print(translation, file=f)
60 | print("{% endmsg %}", file=f)
61 |
--------------------------------------------------------------------------------
/bin/message-stats.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/python3
2 | """
3 | Monitors localization progress.
4 | """
5 | from parsing import parse_messages
6 | from sys import argv
7 | from os.path import isdir
8 |
9 | basepath = "data/page-l10n/" if isdir(".git") else "./"
10 |
11 | def prompt_locale():
12 | from os import listdir
13 | print(
14 | "Please select a locale",
15 | listdir(basepath) if isdir(".git") else "",
16 | end=":\t"
17 | )
18 | return input()
19 |
20 | filename = argv[1] if len(argv) > 1 else prompt_locale()
21 |
22 | translations = [translation for _, _, translation in parse_messages(basepath + filename)]
23 | fraction = "{}/{}".format(
24 | sum(1 for translation in translations if translation), len(translations)
25 | )
26 | print(fraction, "messages translated in", filename)
27 |
--------------------------------------------------------------------------------
/bin/parsing.py:
--------------------------------------------------------------------------------
1 | """
2 | Python module for parsing "Prosody" template files and it's translation catalogues.
3 | """
4 | import re
5 |
6 | tag_re = re.compile("({%x%}|{{x}}|{#x#})".replace("x",
7 | """([^'"]|'([^\\']|\\\\.)*?'|"([^\\"]|\\\\.)*?")*?"""))
8 | def tags(filename):
9 | with open(filename) as f:
10 | line_no = 0
11 | for bit in tag_re.split(f.read()):
12 | if not bit: continue
13 | yield bit, line_no
14 | line_no += bit.count("\n")
15 |
16 | def is_tag(token, tagnames):
17 | if not token.startswith("{%"): return False
18 | token = token[2:-2].strip().split()
19 | return token[0] in tagnames.split()
20 |
21 | def next(template, tagnames):
22 | import codecs
23 | unicode_escape = codecs.getdecoder('unicode_escape')
24 | for token, line_no in template:
25 | if is_tag(token, tagnames):
26 | return line_no, token, True
27 | if token.startswith("{{"):
28 | body = token[2:-2].strip().split("|")
29 | if (body[0][0] in "'\"" and body[0][-1] == body[0][0]
30 | and len(body) >= 2 and body[1] == "trans"):
31 | return line_no, unicode_escape(body[0][1:-1])[0], False
32 | return None, None, None
33 |
34 | def block(template, tagnames):
35 | block = ""
36 | for token, line_no in template:
37 | if is_tag(token, tagnames): return block.strip(), token
38 | else: block += token
39 | raise ValueError("Failed to find endtag out of '{}'".format(tagnames))
40 |
41 | def parse_messages(filename):
42 | catalogue = tags(filename)
43 | line, open_tag, is_tag = next(catalogue, "msg")
44 | while line is not None:
45 | if not is_tag:
46 | raise ValueError("Unexpected variable token on line {}".format(line))
47 |
48 | english, trans_tag = block(catalogue, "trans")
49 | translation, end_tag = block(catalogue, "endmsg")
50 | yield english, open_tag, translation
51 |
52 | line, open_tag, is_tag = next(catalogue, "msg")
53 |
--------------------------------------------------------------------------------
/com.github.alcinnz.odysseus.json:
--------------------------------------------------------------------------------
1 | {
2 | "app-id" : "com.github.alcinnz.odysseus",
3 | "base": "io.elementary.BaseApp",
4 | "base-version": "juno",
5 | "runtime": "org.gnome.Platform",
6 | "sdk": "org.gnome.Sdk",
7 | "runtime-version": "3.32",
8 | "command" : "com.github.alcinnz.odysseus",
9 | "finish-args" : [
10 | "--share=ipc",
11 | "--share=network",
12 | "--socket=x11",
13 | "--socket=wayland",
14 |
15 | "--device=dri",
16 | "--socket=pulseaudio",
17 |
18 | "--filesystem=xdg-run/dconf",
19 | "--filesystem=~/.config/dconf:ro",
20 | "--talk-name=ca.desrt.dconf",
21 | "--env=DCONF_USER_CONFIG_DIR=.config/dconf"
22 | ],
23 | "cleanup" : [
24 | "/include",
25 | "/lib/pkgconfig",
26 | "/share/pkgconfig",
27 | "/share/aclocal",
28 | "/man",
29 | "/share/man",
30 | "/share/gtk-doc",
31 | "*.la",
32 | "*.a",
33 | "/lib/girepository-1.0",
34 | "/share/dbus-1",
35 | "/share/doc",
36 | "/share/gir-1.0"
37 | ],
38 | "modules" : [
39 | {
40 | "name" : "yaml",
41 | "sources" : [
42 | {
43 | "type" : "git",
44 | "url" : "https://github.com/yaml/libyaml.git"
45 | }
46 | ]
47 | },
48 | {
49 | "name" : "appstream",
50 | "buildsystem" : "meson",
51 | "config-opts" : [
52 | "-Dstemming=false",
53 | "-Dvapi=true"
54 | ],
55 | "sources" : [
56 | {
57 | "type" : "git",
58 | "url" : "https://github.com/ximion/appstream.git"
59 | }
60 | ]
61 | },
62 | {
63 | "name" : "odysseus",
64 | "buildsystem" : "meson",
65 | "builddir" : true,
66 | "sources" : [
67 | {
68 | "type" : "git",
69 | "url" : "https://github.com/alcinnz/Odysseus.git"
70 | }
71 | ]
72 | }
73 | ]
74 | }
75 |
--------------------------------------------------------------------------------
/data/Screenshot-download-find.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alcinnz/Odysseus/68029cb93cc08793e8350ee401bec99fca4befdb/data/Screenshot-download-find.png
--------------------------------------------------------------------------------
/data/Screenshot-feed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alcinnz/Odysseus/68029cb93cc08793e8350ee401bec99fca4befdb/data/Screenshot-feed.png
--------------------------------------------------------------------------------
/data/Screenshot-history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alcinnz/Odysseus/68029cb93cc08793e8350ee401bec99fca4befdb/data/Screenshot-history.png
--------------------------------------------------------------------------------
/data/Screenshot-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alcinnz/Odysseus/68029cb93cc08793e8350ee401bec99fca4befdb/data/Screenshot-home.png
--------------------------------------------------------------------------------
/data/com.github.alcinnz.odysseus.desktop.in:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Type=Application
3 | Name=Odysseus
4 | Comment=Surf knowledge, entertainment, and services on The Web
5 | Exec=com.github.alcinnz.odysseus %U
6 | Icon=com.github.alcinnz.odysseus
7 | Terminal=false
8 | Categories=Network;WebBrowser;GTK;GNOME;
9 | # TRANSLATORS This is a semicolon list of keywords for this application
10 | Keywords=WWW;Web;Internet;Browser;
11 | StartupNotify=true
12 | Actions=NewWindow;NewTab;
13 | MimeType=x-scheme-handler/http;x-scheme-handler/https;text/html;application/xhtml+xml;application/x-extension-htm;application/x-extension-html;application/x-extension-shtml;application/x-extension-xht;application/x-extension-mhtml;
14 | X-GNOME-UsesNotifications=true
15 |
16 | [Desktop Action NewWindow]
17 | Name=New Window
18 | Exec=com.github.alcinnz.odysseus --window
19 |
20 | [Desktop Action NewTab]
21 | Name=New Tab
22 | Exec=com.github.alcinnz.odysseus --tab
23 |
--------------------------------------------------------------------------------
/data/db/STYLEGUIDE:
--------------------------------------------------------------------------------
1 | 1. All column names should be lower case with words seperated by underscores.
2 | 2. A line of text should never exceed 80 characters (this is good typography not
3 | just a limitation of old technology).
4 | 3. If a SQL clause is manually wrapped to be within 80 characters, the lines
5 | comprising that clause should be indented by 4 spaces.
6 | 4. The body of a template tag should be indented a further 4 spaces.
7 |
8 | And because we're using SQLite, who have promised not to change their SQL syntax
9 | out from under us:
10 | 1. Don't bother specifying column types - SQLite largely ignores them.
11 | 2. Don't bother specifying a primary key - SQLite provides one called "ROWID"
12 | for it's indices to reference unless you tell it not to.
13 |
--------------------------------------------------------------------------------
/data/db/database.gresource.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | init.sql
5 |
6 |
7 |
--------------------------------------------------------------------------------
/data/meson.build:
--------------------------------------------------------------------------------
1 | icon_sizes = ['16', '24', '32', '48', '64', '128']
2 |
3 | foreach i : icon_sizes
4 | install_data(
5 | join_paths('icons', i, 'com.github.alcinnz.odysseus.svg'),
6 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps')
7 | )
8 | install_data(
9 | join_paths('icons', i, 'com.github.alcinnz.odysseus.svg'),
10 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps')
11 | )
12 | endforeach
13 |
14 | i18n.merge_file(
15 | input: meson.project_name() + '.desktop.in',
16 | output: meson.project_name() + '.desktop',
17 | po_dir: join_paths(meson.source_root(), 'po', 'extra'),
18 | type: 'desktop',
19 | install: true,
20 | install_dir: join_paths(get_option('datadir'), 'applications')
21 | )
22 |
23 | i18n.merge_file(
24 | input: meson.project_name() + '.appdata.xml.in',
25 | output: meson.project_name() + '.appdata.xml',
26 | po_dir: join_paths(meson.source_root(), 'po', 'extra'),
27 | install: true,
28 | install_dir: join_paths(get_option('datadir'), 'metainfo')
29 | )
--------------------------------------------------------------------------------
/data/page-l10n/README:
--------------------------------------------------------------------------------
1 | The files in this directory describe how to translate Oddysseus's internal pages from New Zealand English to various other languages.
2 |
3 | If you want to help teach Oddysseus how to do this, please visit [oddysseus-verbatim:](oddysseus-verbatim:). TODO Implement those pages.
4 |
--------------------------------------------------------------------------------
/data/page-l10n/l10n.gresource.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | fr
5 | nl
6 |
7 |
8 |
--------------------------------------------------------------------------------
/data/pages/INVALID-FORM:
--------------------------------------------------------------------------------
1 | {# This page is triggered by the "psuedorest" trait when SQLite errors out. #}
2 |
3 |
4 |
5 | [dialog-error] {% trans %}INVALID FORM SUBMISSION{% endtrans %}
6 |
10 |
11 |
12 |
13 |
{% trans %}Invalid form submission{% endtrans %}
14 |
{% trans %}The form you just submitted was invalid! While you could
15 | be to blame, this is probably my fault.
16 | {% endtrans %}
17 |
18 |
{% trans %}Please
19 | report this to
20 | us and attach a copy of
21 | this page (use the "Save As" option). More information (in English) is below.
22 | {% endtrans %}
23 |
24 |
25 |
26 |
SQLite Reported:
27 |
{{ error }}
28 |
29 |
{{ sql }}
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/data/pages/NOT-FOUND:
--------------------------------------------------------------------------------
1 | {# This page is shown we we navigate to an invalid odysseus: URI.
2 | The note that this page probably never has existed refers to a promise Odysseus hereby makes
3 | that we'll never outright remove any internal pages. At most we'll leave a tombstone at that URI. #}
4 | {# Please add links to any internal pages you make here, sorting it in alphabetical order.
5 | This helps users get out of this error situation. #}
6 |
7 |
8 |
9 |
10 | [folder]{% trans %}Tool Not Found{% endtrans %}
11 |
12 |
13 |
14 |
15 |
{% trans %}This Page ({{ url }}) Does Not Exist
16 | (and probably never has){% endtrans %}
17 |
{% trans %}Maybe you meant one of these other internal pages?{% endtrans %}
{% trans %}Clears away junk from database.{% endtrans %}
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/data/pages/SERVER-ERROR:
--------------------------------------------------------------------------------
1 | {# This page is shown for any syntax errors in a template.
2 | This should NOT come up for an end-user, as it indicates a bad mess-up on our part.
3 |
4 | As such there is not much that end-user can do except report it to us.
5 | The debugging information is in English in the expectation that any contributors are at least somewhat familiar in that language #}
6 |
7 |
8 |
9 |
10 | [dialog-error]{% trans %}Internal Error{% endtrans %}
11 |
19 |
20 |
21 |
22 |
{% trans %}This Is Bad…{% endtrans %}
23 |
{% trans %}I cannot understand my own code. If you are an Odysseus developer more information (in English) is below. If you aren't, please tell us about the problem. It helps if you can save this page and attach it to your message.{% endtrans %}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/data/pages/errors/201:
--------------------------------------------------------------------------------
1 | {% trans %}Resource successfully created.{% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/202:
--------------------------------------------------------------------------------
1 | {% trans %}The website's still processing you're request.{% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/203:
--------------------------------------------------------------------------------
1 | {% trans %}A proxy server has modified the response you're seeing. (Which for this message to show is presumably empty). {% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/204:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [dialog-information]{% trans %}NO CONTENT{% endtrans %}
6 |
7 |
8 |
9 |
10 |
{% trans %}This Page Has Been Intentionally Left Empty{% endtrans %}
11 |
{% trans %}Until I added this content to let you know so.{% endtrans %}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/data/pages/errors/205.link:
--------------------------------------------------------------------------------
1 | 204
2 |
--------------------------------------------------------------------------------
/data/pages/errors/206:
--------------------------------------------------------------------------------
1 | {% trans %}Partial content received.{% endtrans %}
2 |
3 | {# This page should not show, as the response code implies it has content. #}
4 |
--------------------------------------------------------------------------------
/data/pages/errors/300:
--------------------------------------------------------------------------------
1 | {% trans %}The server says it's presenting you with a choice of formats for this resource, but I don't see any choices. {% endtrans %}
2 | {# Potential icon: dialog-question #}
3 |
--------------------------------------------------------------------------------
/data/pages/errors/301:
--------------------------------------------------------------------------------
1 | {% trans %}Redirecting and updating history to new location…{% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/302:
--------------------------------------------------------------------------------
1 | {% trans %}Redirecting…{% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/303.link:
--------------------------------------------------------------------------------
1 | 302
2 |
--------------------------------------------------------------------------------
/data/pages/errors/304:
--------------------------------------------------------------------------------
1 | {% trans %}An up to date version of this page should still be in my cache. {% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/305:
--------------------------------------------------------------------------------
1 | {% trans %}I'm being told to use a proxy. {% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/307.link:
--------------------------------------------------------------------------------
1 | 302
2 |
--------------------------------------------------------------------------------
/data/pages/errors/308.link:
--------------------------------------------------------------------------------
1 | 301
2 |
--------------------------------------------------------------------------------
/data/pages/errors/400:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [dialog-error]{% trans %}BAD REQUEST{% endtrans %}
5 |
6 |
7 |
8 |
9 |
{% trans %}This Is Apparently Our Fault{% endtrans %}
10 |
{% trans %}Please report the problem and we'll see what we can do about it.{% endtrans %}
{% trans %}You need to log in to access this page.{% endtrans %}
12 |
13 |
14 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/data/pages/errors/402:
--------------------------------------------------------------------------------
1 | {% trans %}The site is requesting you pay to access it's content. However they're not clear how they want you to pay. {% endtrans %}
2 |
3 | {# This error code is not yet widely used on the Web,
4 | though I would love for the W3C or IETF to figure out how it should work.
5 | -- Adrian Cochrane #}
6 | {# Potential icon: credit-card #}
7 |
--------------------------------------------------------------------------------
/data/pages/errors/403:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [locked emblem-unreadable]{% trans %}FORBIDDEN{% endtrans %}
6 |
7 |
8 |
9 |
10 |
{% trans %}Permission Denied{% endtrans %}
11 |
{% trans %}You are not allowed to access this webpage. Try their homepage.{% endtrans %}
{% trans %}Maybe you typed it wrong or followed an invalid link. Or maybe the site you're visiting has inconsiderately moved pages around or even removed them.{% endtrans %}
{% trans %}The Form You Submitted Was Invalid{% endtrans %}
11 |
{% trans %}Please take this up with the website's developers.{% endtrans %}
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/data/pages/errors/406:
--------------------------------------------------------------------------------
1 | {% trans %}The server says it can't give us a file format that we accept. This is odd as, while we have our preferences, we can accept anything. {% endtrans %}
2 |
--------------------------------------------------------------------------------
/data/pages/errors/407:
--------------------------------------------------------------------------------
1 | {% trans %}Proxy authentication required.{% endtrans %} {# TODO how to handle it? #}
2 | {# Potential icon: dialog-password #}
3 |
--------------------------------------------------------------------------------
/data/pages/errors/408:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [task-past-due]{% trans %}REQUEST TIMED OUT{% endtrans %}
5 |
6 |
7 |
8 |
9 |
{% trans %}Timed Out While Asking For This Page{% endtrans %}
{% trans %}This Page Has Been Removed{% endtrans %}
11 |
{% trans %}This page has been purposefully removed, and will not return.{% endtrans %}
12 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/data/pages/errors/411.link:
--------------------------------------------------------------------------------
1 | 400
2 |
--------------------------------------------------------------------------------
/data/pages/errors/412:
--------------------------------------------------------------------------------
1 | {% trans %}Precondition failed.{% endtrans %}
2 |
3 | {# I do not know what this means for a Web Browser -- Adrian Cochrane #}
4 |
--------------------------------------------------------------------------------
/data/pages/errors/413.link:
--------------------------------------------------------------------------------
1 | 405
2 |
--------------------------------------------------------------------------------
/data/pages/errors/414.link:
--------------------------------------------------------------------------------
1 | 405
2 |
--------------------------------------------------------------------------------
/data/pages/errors/415.link:
--------------------------------------------------------------------------------
1 | 405
2 |
--------------------------------------------------------------------------------
/data/pages/errors/416:
--------------------------------------------------------------------------------
1 | {% trans %}Could not download selected file segment. {% endtrans %}
2 |
3 | {# We probably will not get this for HTML,
4 | as WebKit downloads the full thing in one go. #}
5 |
--------------------------------------------------------------------------------
/data/pages/errors/417:
--------------------------------------------------------------------------------
1 | {% trans %}The conditions we have set failed. {% endtrans %}
2 |
3 | {# I do not think WebKit sets any conditions #}
4 |
--------------------------------------------------------------------------------
/data/pages/errors/418:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [dialog-information]{% trans %}I'm A Teapot{% endtrans %}
6 |
10 |
11 |
12 |
13 |
{% trans %}{# Easter-egg 418: verse 1 of "I'm A Teapot" nursery rhyme #}
14 | I'm a little teapot
15 | Short and stout
16 | Here is my handle
17 | And here is my spout{% endtrans %}
18 |
{% trans %}{# Easter-egg 418: verse 2 of "I'm A Teapot" nursery rhyme #}
19 | When I get all steamed up
20 | Hear me shout
21 | "Tip me over
22 | And pour me out!"{% endtrans %}
23 |
{% trans %}{# Easter-egg 418: attribution #}
24 | April Fools!
25 | From the IETF.
26 | {% endtrans %}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/data/pages/errors/421.link:
--------------------------------------------------------------------------------
1 | 500
2 |
--------------------------------------------------------------------------------
/data/pages/errors/422:
--------------------------------------------------------------------------------
1 |
2 |
3 | {# Indicates that the semantics of the submitted data are invalid.
4 | Does not provide much more information so
5 | we will make it appear that the form did not submit. #}
6 |
--------------------------------------------------------------------------------
/data/pages/errors/423:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [locked]{% trans %}LOCKED{% endtrans %}
6 |
7 |
8 |
9 |
10 |
{% trans %}This WebDAV Page is Locked{% endtrans %}
11 |
{% trans %}Try making your changes again later, or take it up with your group.{% endtrans %}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/data/pages/errors/507.icon:
--------------------------------------------------------------------------------
1 | dialog-error
2 |
--------------------------------------------------------------------------------
/data/pages/errors/508.link:
--------------------------------------------------------------------------------
1 | 500
2 |
--------------------------------------------------------------------------------
/data/pages/errors/510.link:
--------------------------------------------------------------------------------
1 | protocol
2 |
--------------------------------------------------------------------------------
/data/pages/errors/511:
--------------------------------------------------------------------------------
1 | {% trans %}The WiFi authentication window should have opened…{% endtrans %}
2 |
3 | {# Potential icon: preferences-system-network #}
4 |
--------------------------------------------------------------------------------
/data/pages/errors/README.md:
--------------------------------------------------------------------------------
1 | This folder contains the HTML/Templated error messages for various network errors.
2 |
3 | The goals are to be thorough in regards to the errors that might occur, non-technical in the explanations, and helpful in getting out of these situations. Beyond that it helps to be personable (which some may find funny) as that not only helps relieve frustration but makes it easier to be nontechnical. Furthermore the page title should be in all-uppercase in order to differentiate error pages from pretty much all other web pages.
4 |
5 | Currently this folder contains files for all the standard HTTP error statuses (though some which are almost certainly not going to be encountered aren't HTML formatted), and all the errors WebKit might report to us.
6 |
7 | ASSIGNING ICONS
8 | ---------------
9 |
10 | WebKit refuses to show icons from different domains, and will interpret any common icons we provide as being on a different domain. As such, we don't use favicon links to assign URIs. Instead we specify the icons as seperate resources, which tells our chrome (instead of WebKit) which system icon to use. Besides this techique is more succinct and can in total save a fair few bytes.
11 |
12 | The logic here ensures that these pages uses the more simplistic symbolic icons where they exist, again to contrast against tendancies on the broader web.
13 |
14 | Templating
15 | ----------
16 |
17 | When one of these pages show, we want to help the reader fix the problem as quickly as possible. To make it easier for us to help them the base URL for any of these pages is that of the erroring page (this means no templating or JavaScript is required to reload the page or navigate to another page on the site). Furthermore the "url" outputs the page's URL while allowing you to use subproperties to access it's different components.
18 |
--------------------------------------------------------------------------------
/data/pages/errors/bad-certificate:
--------------------------------------------------------------------------------
1 | {# Displayed when certificate validation fails.
2 | Most browsers communicate this [fairly poorly](https://blogs.gnome.org/mcatanzaro/2016/03/12/do-you-trust-this-website/), so I get right to the point that (in nontechnical language) there might be a man-in-the-middle attack.
3 |
4 | Furthermore the psuedo-conversation users must engage in to bypass this warning clearly indicates the danger (adding an extra step to indicate this) and when it is appropriate to bypass this security check (that is bypass for sites you DO NOT trust, not for ones you do).
5 |
6 | In case users want the technical details, a link to Wikipedia is provided. #}
7 |
8 |
9 |
10 | [channel-insecure]{% trans %}INVALID CERTIFICATE{% endtrans %}
11 |
14 |
15 |
16 |
17 |
{% trans %}This site may not be who it appears to be{% endtrans %}
18 |
{% trans %}It has failed to assert it's own identity.
19 | This may be due to its configuration, or it may be because someone's actively trying to spy on you.{% endtrans %}
{% trans %}But I don't understand what it was.{% endtrans %}
5 |
--------------------------------------------------------------------------------
/data/pages/ext/README.md:
--------------------------------------------------------------------------------
1 | This folder contains third party JavaScript (and/or CSS) libraries used in Odysseus's internal pages.
2 |
3 | So far they include:
4 |
5 |
Adds syntax highlighting for view source and debugging pages.
8 |
As opposed to what Vala has for syntax highlighting (the Gtk.SourceView) this can work with HTML/WebKit as opposed to being it's own custom control.
9 |
10 |
11 | In using a library please make sure it doesn't come with functions we won't use, as we should be cogniscent of our download size. At the same time the user won't be waiting for Odysseus to download each time they use it, so we can afford to use the uncompressed versions of our libraries.
12 |
--------------------------------------------------------------------------------
/data/pages/ext/highlight.pack.js.mime:
--------------------------------------------------------------------------------
1 | text/javascript
2 |
--------------------------------------------------------------------------------
/data/pages/ext/hljs-solarized-light.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull
4 |
5 | */
6 |
7 | .hljs {
8 | display: block;
9 | overflow-x: auto;
10 | padding: 0.5em;
11 | background: #fdf6e3;
12 | color: #657b83;
13 | }
14 |
15 | .hljs-comment,
16 | .hljs-quote {
17 | color: #93a1a1;
18 | }
19 |
20 | /* Solarized Green */
21 | .hljs-keyword,
22 | .hljs-selector-tag,
23 | .hljs-addition {
24 | color: #859900;
25 | }
26 |
27 | /* Solarized Cyan */
28 | .hljs-number,
29 | .hljs-string,
30 | .hljs-meta .hljs-meta-string,
31 | .hljs-literal,
32 | .hljs-doctag,
33 | .hljs-regexp {
34 | color: #2aa198;
35 | }
36 |
37 | /* Solarized Blue */
38 | .hljs-title,
39 | .hljs-section,
40 | .hljs-name,
41 | .hljs-selector-id,
42 | .hljs-selector-class {
43 | color: #268bd2;
44 | }
45 |
46 | /* Solarized Yellow */
47 | .hljs-attribute,
48 | .hljs-attr,
49 | .hljs-variable,
50 | .hljs-template-variable,
51 | .hljs-class .hljs-title,
52 | .hljs-type {
53 | color: #b58900;
54 | }
55 |
56 | /* Solarized Orange */
57 | .hljs-symbol,
58 | .hljs-bullet,
59 | .hljs-subst,
60 | .hljs-meta,
61 | .hljs-meta .hljs-keyword,
62 | .hljs-selector-attr,
63 | .hljs-selector-pseudo,
64 | .hljs-link {
65 | color: #cb4b16;
66 | }
67 |
68 | /* Solarized Red */
69 | .hljs-built_in,
70 | .hljs-deletion {
71 | color: #dc322f;
72 | }
73 |
74 | .hljs-formula {
75 | background: #eee8d5;
76 | }
77 |
78 | .hljs-emphasis {
79 | font-style: italic;
80 | }
81 |
82 | .hljs-strong {
83 | font-weight: bold;
84 | }
85 |
--------------------------------------------------------------------------------
/data/pages/ext/hljs-solarized-light.css.mime:
--------------------------------------------------------------------------------
1 | text/css
2 |
--------------------------------------------------------------------------------
/data/pages/ext/html5sortable.js.mime:
--------------------------------------------------------------------------------
1 | text/javascript
2 |
--------------------------------------------------------------------------------
/data/pages/feedreaders:
--------------------------------------------------------------------------------
1 | {# Linked to by subscribe button if no feedreaders are available. #}
2 |
3 |
4 |
5 | [application-rss+xml] {% trans %}Available feedreaders{% endtrans %}
6 |
10 |
11 |
12 |
13 |
{% trans %}
14 | No feed readers are installed
15 | {% endtrans %}
16 |
{% trans %}
17 | In order to subscribe to a webfeed,
18 | please install one first.
19 | {% endtrans %}
20 |
21 | {% appstream "application/rss+xml;application/atom+xml" %}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/data/pages/filetype:
--------------------------------------------------------------------------------
1 | {# Displayed in the absence of an installed app that can handle the URI #}
2 |
3 |
4 |
5 | [unknown] {% trans %}CAN'T OPEN {{ filetype }} FILES{% endtrans %}
6 |
11 |
12 |
13 | {% with filetype=url.query.mime %}
14 |
15 | {% macro filetype %}
16 | {{ filetype }}
17 | {% mimeinfo filetype %}
18 | {% endmacro %}
19 | {% trans %}
20 | {% filetype %} files cannot be opened with any software on this computer
21 | {% endtrans %}
22 |
23 |
{% trans %}
24 | You must install a compatible app before your download will open.
25 | {% endtrans %}
26 |
27 | {% appstream filetype %}
28 | {% endwith %}
29 |
30 |
31 |
--------------------------------------------------------------------------------
/data/pages/history-DESIGN:
--------------------------------------------------------------------------------
1 | Why is odysseus:history designed the way it is?
2 |
3 | As mentioned in the file, my main goal is to make it make it easy for your eyes
4 | to land on the correct entry. And secondly I'm guided by an assumption that
5 | surfers have some idea of what they are looking for.
6 |
7 | And to do so (help you find the right entry) I make use of emphasis to highlight
8 | changes in the hours/day/month/year the page was visited, and use colour to give
9 | some indication of traversals between pages. Limiting these indicators to
10 | typography reduces visual noise and leaves the focus on the actual content.
11 |
12 | The paging was added to handle a freeze caused by WebKit struggling to read in
13 | your full browser history (rendered to HTML) in a single go. A date range is
14 | added to the header in part because it's easier to design around the flakiness of
15 | the first iteration of the {% ifchanged %} tag in this way then to actually fix
16 | it. But also it helps to indicate whether the page your looking for will be on
17 | this page. For similar reasons the pagination links are tooltipped with their
18 | date ranges to help surfers find the correct one to click, but otherwise they
19 | need no label - just a mark.
20 |
21 | Search was added so surfers can tell the computer what that idea of what they
22 | want to find again, and screenshots were added to give an indication of whether
23 | they've found the right entry.
24 |
--------------------------------------------------------------------------------
/data/pages/special/README:
--------------------------------------------------------------------------------
1 | This folder contains templates that require specialized input data.
2 |
3 | Do not try to navigate to these directly, they'll appear blank.
4 |
--------------------------------------------------------------------------------
/data/pages/special/applist:
--------------------------------------------------------------------------------
1 |
2 | {% trans %}Compatible apps{% endtrans %}
3 |
4 |
17 |
--------------------------------------------------------------------------------
/data/pages/special/restore:
--------------------------------------------------------------------------------
1 | {# Startup performance optimization to tell WebKit to delay loading this page
2 | until it is clear that the user wants it to load #}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/data/pages/special/viewsource:
--------------------------------------------------------------------------------
1 | {# Renders view-source in the browser, triggered by the view-source menu item with custom variables.
2 | Odysseus does it this way, as opposed to using Scratch as both applications are capable of rendering text
3 | and Odysseus is better suited to the Read-only nature of this feature.
4 |
5 | The styling of this page matches Scratch''s defaults.
6 |
7 | A custom favicon is used to indicate this is a view-source tab and
8 | information about the source page is displayed in the usual places.
9 | Line numbers are not included as the developer of our syntax highlighting library
10 | wishes to discourage them and I (Adrian Cochrane) am not missing them. #}
11 |
12 |
13 |
14 | [text-html]{{ title }}
15 |
16 |
24 |
25 |
26 |
27 |
28 |
{{ source }}
29 |
30 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/data/pages/spring-clean:
--------------------------------------------------------------------------------
1 | {% random %}
2 | {# Load up the full-text search index on-demand #}
3 | {% query %}INSERT OR IGNORE INTO history_fts(rowid, uri, title)
4 | SELECT rowid, uri, title FROM page_visit;{% endquery %}
5 | {% alt %}
6 | {# Update the visit_counts index. This can take a little bit but not so much it is noticable.
7 | Formulating it this way regularly gives control back to the GLib MainLoop,
8 | such that the application remains responsive.
9 | i.e. SQLite can freeze Odysseus, but Prosody cannot. #}
10 | {% query %}BEGIN TRANSACTION;{% endquery %}
11 | {# visit_counts is a special index, akin to a materialized view, that optimizes
12 | selecting/ordering pages based on number of visits. #}
13 | {% query %}SELECT uri, COUNT(*) AS count FROM page_visit GROUP BY uri;
14 | {% except %}SELECT * FROM visit_counts WHERE url = {{ uri }} AND count = {{ count }};
15 | {% each-row %}
16 |
17 | {% query %}INSERT OR REPLACE INTO visit_counts(url, count)
18 | VALUES ({{ uri }}, {{ count }});{% endquery %}
19 | {% endquery %}
20 | {% query %}END TRANSACTION;{% endquery %}
21 | {% alt %}
22 | {% query %}
23 | DELETE FROM link_sources WHERE link IN (
24 | SELECT unvisited_links.rowid
25 | FROM unvisited_links, page_visit
26 | WHERE page_visit.uri = unvisited_links.uri);
27 | DELETE FROM unvisited_links WHERE uri IN (SELECT uri FROM page_visit);
28 | {% endquery %}
29 | {% endrandom %}
30 |
31 | {% trans %}Spring cleaning finished!{% endtrans %}
32 |
--------------------------------------------------------------------------------
/data/pages/suggestions:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [mail-unread] {% trans %}Personalized Suggestions{% endtrans %}
5 |
6 |
7 |
8 |
{% trans %}Personalized Suggestions{% endtrans %}
9 |
{% trans %}You have encountered, but not visited, these links whilst surfing the Web.{% endtrans %}
10 |
11 |
{% query %}
12 | SELECT rowid, uri FROM unvisited_links
13 | WHERE uri NOT IN (SELECT uri FROM page_visit)
14 | ORDER BY endorsements DESC, random()
15 | LIMIT 25;
16 | {% each-row %}
17 |
{% trans %}I don't seem to have any links to recommend to you specifically,
31 | but here's some great ones!
32 | {% endtrans %}
33 | {% endquery %}
34 |
35 |
36 |
--------------------------------------------------------------------------------
/data/pages/view:
--------------------------------------------------------------------------------
1 | {# Various MIMEtypes will have special meaning to Odysseus,
2 | but renders really poorly in WebKit.
3 |
4 | So the viewers directory provides alternate renderers for those,
5 | and this template loads the appropriate one. #}
6 |
7 | {% if url.query.url %}
8 | {% with uri=url Q=url.query url=url.query.url %}
9 |
10 | {% fetch %}{{ url|safe }}
11 | {% each as page mime %}
12 | {% include "viewers/" mime %}
13 | {% endfetch %}
14 | {% endwith %}
15 | {% else %}
16 |
17 | {% endif %}
18 |
--------------------------------------------------------------------------------
/data/pages/viewers/application/atom+xml:
--------------------------------------------------------------------------------
1 | {% with format="xhtml" %}{% include "rss+xml" %}{% endwith %}
2 |
--------------------------------------------------------------------------------
/data/pages/viewers/application/xml:
--------------------------------------------------------------------------------
1 | {# Try to infer type if server responds with an XML file. Otherwise viewsource. #}
2 | {% if page.channel %}{% include "../application/rss+xml" %}
3 | {% else %}{{ xml }}{% endif %}
4 |
--------------------------------------------------------------------------------
/data/pages/viewers/text/xml:
--------------------------------------------------------------------------------
1 | {# Try to infer type if server responds with an XML file. Otherwise viewsource. #}
2 | {% if page.channel %}{% include "../application/rss+xml" %}
3 | {% else %}{{ xml }}{% endif %}
4 |
--------------------------------------------------------------------------------
/data/pages/webfeed-subscribe-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | com.github.alcinnz.odysseus (1.0.0) precise; urgency=low
2 |
3 | * Initial Release.
4 |
5 | -- Adrian Cochrane Fri, 22 Dec 2017 07:53:59 +1300
6 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 9
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: com.github.alcinnz.odysseus
2 | Section: x11
3 | Priority: extra
4 | Maintainer: Adrian Cochrane
5 | Build-Depends: meson,
6 | debhelper (>= 9),
7 | libgtk-3-dev,
8 | libgranite-dev (>= 0.3.0),
9 | valac (>= 0.26),
10 | libwebkit2gtk-4.0-dev (>= 2.10.9),
11 | libjson-glib-dev (>= 1.1.2),
12 | libsoup2.4-dev (>= 2.52.2),
13 | libsqlite3-dev (>= 3.11.0),
14 | libappstream-dev (>= 0.10),
15 | libgcr-3-dev (>= 3.18.0)
16 | Standards-Version: 3.9.3
17 |
18 | Package: com.github.alcinnz.odysseus
19 | Architecture: any
20 | Depends: ${misc:Depends}, ${shlibs:Depends}, html-xml-utils (>= 7)
21 | Description: A simple and performant yet powerful window onto the open decentralized web
22 | Odysseus is a convenient and privacy-respecting web browser, that increasingly,
23 | gently, and unobtrusively guides you wherever you want to go online.
24 | Through this well thought out simplicity Odysseus lets you focus on the webpages
25 | that matter to you.
26 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: http://dep.debian.net/deps/dep5
2 | Upstream-Name: io.github.alcinnz.Odysseus
3 | Source: https://alcinnz.github.io/Odysseus
4 |
5 | Files: *
6 | Copyright: 2017 Adrian Cochrane
7 | License: GPL-3.0+
8 |
9 | License: GPL-3.0+
10 | This program is free software: you can redistribute it and/or modify
11 | it under the terms of the GNU General Public License as published by
12 | the Free Software Foundation, either version 3 of the License, or
13 | (at your option) any later version.
14 | .
15 | This package is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | GNU General Public License for more details.
19 | .
20 | You should have received a copy of the GNU General Public License
21 | along with this program. If not, see .
22 | .
23 | On Debian systems, the complete text of the GNU General
24 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
25 |
26 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | # -*- makefile -*-
3 | # Sample debian/rules that uses debhelper.
4 | # This file was originally written by Joey Hess and Craig Small.
5 | # As a special exception, when this file is copied by dh-make into a
6 | # dh-make output file, you may use that output file without restriction.
7 | # This special exception was added by Craig Small in version 0.37 of dh-make.
8 |
9 | # This file was extended to incorporate a Meson/Ninja build system.
10 |
11 | # Uncomment this to turn on verbose mode.
12 | #export DH_VERBOSE=1
13 |
14 | %:
15 | dh $@
16 |
17 | override_dh_auto_clean:
18 | rm -rf debian/build
19 |
20 | override_dh_auto_configure:
21 | mkdir -p debian/build
22 | cd debian/build && meson --prefix=/usr ../..
23 |
24 | override_dh_auto_build:
25 | cd debian/build && ninja -v
26 |
27 | override_dh_auto_test:
28 | cd debian/build && ninja test
29 |
30 | override_dh_auto_install:
31 | cd debian/build && DESTDIR=${CURDIR}/debian/com.github.alcinnz.odysseus ninja install
32 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (native)
2 |
--------------------------------------------------------------------------------
/default.nix:
--------------------------------------------------------------------------------
1 | import ./nix {}
2 |
--------------------------------------------------------------------------------
/make-full.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | python3 bin/extract-message.py
3 |
4 | cd build
5 | ninja com.github.alcinnz.odysseus-pot
6 | ninja extra-pot
7 | ninja
8 |
9 | echo "Successfully built Odysseus!"
10 | echo "You may now commit your changes and ``sudo ninja install`` from ./build"
11 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project('com.github.alcinnz.odysseus', ['vala', 'c'],
2 | version: '1.0.0',
3 | license: 'GNU',
4 | meson_version: '>=0.40.1')
5 |
6 | gnome = import('gnome')
7 | i18n = import('i18n')
8 |
9 | about_pages = gnome.compile_resources(
10 | 'about-pages', 'data/pages/about-pages.gresource.xml',
11 | source_dir: 'data/pages',
12 | c_name: 'ap')
13 | sql_scripts = gnome.compile_resources(
14 | 'sql-scripts', 'data/db/database.gresource.xml',
15 | source_dir: 'data/db',
16 | c_name: 'sqlscripts')
17 | l10n_pages = gnome.compile_resources(
18 | 'page-l10n', 'data/page-l10n/l10n.gresource.xml',
19 | source_dir: 'data/page-l10n',
20 | c_name: 'pagel10n')
21 |
22 | conf = configuration_data()
23 | conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
24 | configure_file(output: 'config.h', configuration: conf)
25 | config_h_dir = include_directories('.')
26 |
27 | subdir('data')
28 | subdir('po')
29 | subdir('src')
30 |
--------------------------------------------------------------------------------
/meson_options.txt:
--------------------------------------------------------------------------------
1 | option('appstream', type: 'boolean', value: true, description: 'Use appstream to recommend compatible apps')
2 | option('gcr', type: 'boolean', value: true, description: 'Use GCR to show certificate details, HEAVILY RECOMMENDED')
3 |
--------------------------------------------------------------------------------
/nix/.gitignore:
--------------------------------------------------------------------------------
1 | /result
2 |
--------------------------------------------------------------------------------
/nix/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs ? import ./nixpkgs {}
2 | , lib ? pkgs.lib
3 | , makeScope ? lib.makeScope
4 | , newScope ? pkgs.newScope
5 | , pantheon ? pkgs.pantheon
6 | }:
7 |
8 | with makeScope newScope (self: with self; {
9 | inherit (pantheon) granite vala;
10 | odysseus = self.callPackage ./odysseus.nix {};
11 | });
12 |
13 | odysseus
14 |
--------------------------------------------------------------------------------
/nix/nixpkgs/.gitignore:
--------------------------------------------------------------------------------
1 | /result
2 |
--------------------------------------------------------------------------------
/nix/nixpkgs/bump.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env nix-shell
2 | #! nix-shell --quiet -p bash gawk git nix -i bash
3 |
4 | set -euo pipefail
5 |
6 | BASE_URL=https://github.com/NixOS/nixpkgs-channels
7 | DEFAULT_REV=refs/heads/nixpkgs-unstable
8 | NAME=source
9 |
10 | cd "$(dirname "${BASH_SOURCE[0]}")"
11 |
12 | rev=${1:-$DEFAULT_REV}
13 |
14 | if (( ${#rev} != 40 )); then
15 | rev=$(git ls-remote "$BASE_URL" | awk -v rev="$rev" '$2 == rev { print $1 }')
16 | fi
17 |
18 | url=$BASE_URL/archive/$rev.tar.gz
19 |
20 | sha256=$(nix-prefetch-url --unpack "$url")
21 |
22 | cat > url.json.new <
2 | }:
3 |
4 | fetchurl (builtins.fromJSON (builtins.readFile ./url.json))
5 |
--------------------------------------------------------------------------------
/nix/odysseus.nix:
--------------------------------------------------------------------------------
1 | { stdenv, substituteAll
2 | , appstream, cmake, gcr, gettext, glib, granite, gtk3
3 | , html-xml-utils, json-glib, libgee, libsoup, meson, ninja, pkgconfig, python3
4 | , sqlite, vala, webkitgtk, glib-networking, wrapGAppsHook
5 | }:
6 |
7 | stdenv.mkDerivation {
8 | name = "odysseus";
9 |
10 | src = ./..;
11 |
12 | patches = [
13 | (substituteAll {
14 | src = ./patches/hxwls-path.patch;
15 | hxwls = "${html-xml-utils}/bin/hxwls";
16 | })
17 | ];
18 |
19 | nativeBuildInputs = [
20 | gettext
21 | meson
22 | ninja
23 | pkgconfig
24 | python3
25 | vala
26 | wrapGAppsHook
27 | ];
28 |
29 | buildInputs = [
30 | appstream
31 | gcr
32 | glib
33 | glib-networking
34 | granite
35 | gtk3
36 | json-glib
37 | libgee
38 | libsoup
39 | sqlite
40 | webkitgtk
41 | ];
42 |
43 | meta = with stdenv.lib; {
44 | description = "A simple and performant yet powerful elementary OS-style window onto the open decentralized web";
45 | homepage = "https://odysseus.adrian.geek.nz";
46 | license = licenses.gpl3Plus;
47 | platforms = platforms.linux;
48 | maintainers = [ maintainers.worldofpeace ];
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/nix/patches/hxwls-path.patch:
--------------------------------------------------------------------------------
1 | diff --git a/src/Models/Links.vala b/src/Models/Links.vala
2 | index 1bbf9eb..f7f6668 100644
3 | --- a/src/Models/Links.vala
4 | +++ b/src/Models/Links.vala
5 | @@ -29,7 +29,7 @@ namespace Odysseus.Model {
6 | int stdin;
7 | int stdout;
8 | Process.spawn_async_with_pipes("/",
9 | - {"hxwls", "-lb", url},
10 | + {"@hxwls@", "-lb", url},
11 | null,
12 | SpawnFlags.SEARCH_PATH,
13 | null,
14 |
--------------------------------------------------------------------------------
/po/LINGUAS:
--------------------------------------------------------------------------------
1 | cs
2 | fr
3 | nl
4 |
--------------------------------------------------------------------------------
/po/POTFILES:
--------------------------------------------------------------------------------
1 | src/BrowserWindow.vala
2 | src/Odysseus.vala
3 | src/Models/Download.vala
4 | src/Widgets/DownloadBar.vala
5 | src/Widgets/DownloadButton.vala
6 | src/Widgets/WebTab.vala
7 | src/Widgets/Chromeless.vala
8 | src/Widgets/overlays/FindToolbar.vala
9 | src/Widgets/overlays/InfoContainer.vala
10 | src/Traits/download-progress.vala
11 | src/Traits/download-window.vala
12 | src/Traits/decorate/alert.vala
13 | src/Traits/decorate/permit.vala
14 | src/Traits/navigate/errors.vala
15 | src/Traits/navigate/viewsource.vala
16 | src/Traits/status/https.vala
17 | src/Traits/status/local.vala
18 | src/Traits/status/webfeed.vala
19 | src/Traits/status/liberate.vala
20 |
--------------------------------------------------------------------------------
/po/extra/LINGUAS:
--------------------------------------------------------------------------------
1 | cs
2 | fr
3 | nl
4 | pt
5 |
--------------------------------------------------------------------------------
/po/extra/POTFILES:
--------------------------------------------------------------------------------
1 | data/com.github.alcinnz.odysseus.desktop.in
2 | data/com.github.alcinnz.odysseus.appdata.xml.in
3 |
--------------------------------------------------------------------------------
/po/extra/meson.build:
--------------------------------------------------------------------------------
1 |
2 | i18n.gettext('extra',
3 | args: [
4 | '--directory=' + meson.source_root(),
5 | '--from-code=UTF-8'
6 | ],
7 | preset: 'glib',
8 | install: false
9 | )
--------------------------------------------------------------------------------
/po/meson.build:
--------------------------------------------------------------------------------
1 |
2 | i18n.gettext(meson.project_name(),
3 | args: [
4 | '--directory=' + meson.source_root(),
5 | '--from-code=UTF-8'
6 | ],
7 | preset: 'glib'
8 | )
9 |
10 | subdir('extra')
--------------------------------------------------------------------------------
/src/Models/DownloadSet.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Manages a set of Downloads, whilst emitting signals that are easy tie into a
18 | hierarchical GTK UI.
19 |
20 | The maintained set does not include successfully downloaded files, because if those
21 | are kept in the downloads bar the UI may become cluttered. */
22 | public class Odysseus.DownloadSet : Object {
23 | public Gee.ArrayList downloads = new Gee.ArrayList();
24 | public virtual signal void add(Download item) {
25 | downloads.add(item);
26 | item.cancel.connect(() => downloads.remove(item));
27 | item.finished.connect(() => downloads.remove(item));
28 | }
29 |
30 | private static DownloadSet? instance;
31 | public static DownloadSet get_downloads() {
32 | if (instance == null) instance = new DownloadSet();
33 | return instance;
34 | }
35 |
36 | public static void setup_ctx(WebKit.WebContext ctx) {
37 | var downloads = get_downloads();
38 | ctx.download_started.connect((download) => downloads.add(new Download(download)));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Models/I18nUtil.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Implies additional fallback languages from the environment-specified locale.
18 | This is necessary for the localization of:
19 | * Recommendations from https://alcinnz.github.io/Odysseus-recommendations
20 | * XML and potentially other formats
21 | * Templates */
22 | namespace Odysseus.I18n {
23 | public string[] get_locales() {
24 | var locales = Intl.get_language_names();
25 | var ret = new Gee.ArrayList.wrap(locales);
26 | var added = new Gee.HashSet();
27 |
28 | for (var i = locales.length - 1; i >= 0; i--) {
29 | var sep = locales[i].index_of_char('_');
30 | if (sep == -1) {added.add(locales[i]); continue;}
31 |
32 | var fallback = locales[i][0:sep];
33 | if (fallback in added) continue;
34 |
35 | added.add(fallback);
36 | ret.insert(i, fallback);
37 | }
38 | return ret.to_array();
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Models/ImageUtil.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Recolour often third-party icons to use the elementary colour scheme.
18 | https://elementary.io/docs/human-interface-guidelines#color
19 |
20 | For third party icons this reduces obtrusiveness whilst maintaining a
21 | recognizable shape. At the same it can be used with built-in icons for an
22 | extra channel of communication.
23 |
24 | The latter isn't used yet.*/
25 | namespace Odysseus.ImageUtil {
26 | public Gdk.Pixbuf recolour(Gdk.Pixbuf source, string colour) {
27 | // White is often used to define the shape via it's "whitespace".
28 | // This preprocessing step maintains that function.
29 | var mask = source.add_alpha(true, 255, 255, 255);
30 |
31 | // Cairo has a handy mask() method for this.
32 | var dest = new Cairo.ImageSurface(Cairo.Format.ARGB32,
33 | source.width, source.height);
34 | var ctx = new Cairo.Context(dest);
35 | var g_colour = Gdk.RGBA();
36 | g_colour.parse(colour);
37 | Gdk.cairo_set_source_rgba(ctx, g_colour);
38 | ctx.mask_surface(Gdk.cairo_surface_create_from_pixbuf(mask, 0, null), 0, 0);
39 |
40 |
41 | return Gdk.pixbuf_get_from_surface(dest, 0, 0, source.width, source.height);
42 | }
43 |
44 | // GDK does provide a utility for this,
45 | // but it requires me to specify size information I do not have.
46 | public static Gdk.Pixbuf? surface_to_pixbuf(Cairo.Surface surface) {
47 | try {
48 | var loader = new Gdk.PixbufLoader.with_mime_type("image/png");
49 | surface.write_to_png_stream((data) => {
50 | try {
51 | loader.write((uint8[]) data);
52 | } catch (Error e) {
53 | return Cairo.Status.DEVICE_ERROR;
54 | }
55 | return Cairo.Status.SUCCESS;
56 | });
57 | var pixbuf = loader.get_pixbuf();
58 | loader.close();
59 | return pixbuf;
60 | } catch (Error e) {
61 | return null;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Models/Links.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2019).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | namespace Odysseus.Model {
18 | public class Link {
19 | public string tag;
20 | public string rel;
21 | public string href;
22 | public Link(string tag, string rel, string href) {
23 | this.tag = tag; this.rel = rel; this.href = href;
24 | }
25 | }
26 | public async Link[] parse_links(uint8[] src, string url) {
27 | // Runs a W3C utility to extract additional information WebKit won't
28 | // readily give me.
29 | try {
30 | int stdin;
31 | int stdout;
32 | Process.spawn_async_with_pipes("/",
33 | {"hxwls", "-lb", url},
34 | null,
35 | SpawnFlags.SEARCH_PATH,
36 | null,
37 | null,
38 | out stdin,
39 | out stdout,
40 | null);
41 | var hxwls_in = new UnixOutputStream(stdin, true);
42 | var hxwls_out = new DataInputStream(new UnixInputStream(stdout, true));
43 |
44 | // Fix for webpages with, say, megabytes of HTML, so they don't freeze.
45 | // Scanning just the first several KB should be very generous.
46 | if (src.length > 8*1024) {
47 | yield hxwls_in.write_all_async(src[0:8*1024], Priority.DEFAULT, null, null);
48 | } else {
49 | yield hxwls_in.write_all_async(src, Priority.DEFAULT, null, null);
50 | }
51 | yield hxwls_in.close_async(Priority.DEFAULT, null);
52 |
53 | var links = new Gee.ArrayList();
54 | string line;
55 | while ((line = yield hxwls_out.read_line_async()) != null) {
56 | var record = line.split("\t");
57 | links.add(new Link(record[0], record[1], record[2]));
58 | }
59 | return links.to_array();
60 | } catch (Error err) {
61 | warning("Failed to parse links from page source: %s", err.message);
62 | }
63 | return new Link[0];
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Services/Prosody/misc/favicons.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Template filter for accessing the WebKit Favicon Database. */
18 | namespace Odysseus.Templating.x {
19 |
20 | /** Translates a URI into one for it's favicon. */
21 | private class FaviconFilter : Filter {
22 | public override Data.Data filter0(Data.Data a) {
23 | var db = get_web_context().get_favicon_database();
24 | var uri = db.get_favicon_uri(@"$a");
25 | if (uri == null) {
26 | var soup = new Soup.URI(@"$a");
27 | soup = new Soup.URI.with_base(soup, "/favicon.ico");
28 | uri = soup.to_string(false);
29 | }
30 | return new Data.Literal(uri);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Services/Prosody/misc/json.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | /** A datasource to expose JSON data to templates.
19 |
20 | This is particularly vital for running the test suite. */
21 | namespace Odysseus.Templating.xJSON {
22 | using Data;
23 |
24 | public Data.Data build(Json.Node? node) {
25 | if (node == null) return new Empty();
26 | switch (node.get_node_type()) {
27 | case Json.NodeType.OBJECT:
28 | return new Object(node.dup_object());
29 | case Json.NodeType.ARRAY:
30 | return new Array(node.dup_array());
31 | case Json.NodeType.VALUE:
32 | return new Literal(node.get_value());
33 | case Json.NodeType.NULL:
34 | return new Empty();
35 | default:
36 | return new Empty();
37 | }
38 | }
39 |
40 | private class Array : Data.Data {
41 | Json.Array inner;
42 | public Array(Json.Array a) {this.inner = a;}
43 |
44 | public override Data.Data get(Slice property_bytes) {
45 | var property = @"$property_bytes";
46 | uint64 index = 0;
47 | if (property[0] == '$' &&
48 | uint64.try_parse(property[1:property.length], out index) &&
49 | index < inner.get_length()) {
50 | return build(inner.get_element((uint) index));
51 | }
52 | return new Empty();
53 | }
54 |
55 | public override void @foreach(Data.Data.Foreach cb) {
56 | range(cb, inner.get_length());
57 | }
58 |
59 | public override Gee.SortedSet items() {
60 | var ret = new Gee.TreeSet();
61 | for (uint i = 0; i < inner.get_length(); i++) {
62 | if (inner.get_string_element(i) != null)
63 | ret.add(inner.get_string_element(i));
64 | }
65 | return ret;
66 | }
67 |
68 | public override int to_int(out bool is_length = null) {
69 | is_length = true;
70 | return (int) inner.get_length();
71 | }
72 | }
73 |
74 | private class Object : Data.Data {
75 | Json.Object inner;
76 | public Object(Json.Object o) {this.inner = o;}
77 |
78 | public override Data.Data get(Slice property) {
79 | return build(inner.dup_member(@"$property"));
80 | }
81 |
82 | public override void @foreach(Data.Data.Foreach cb) {
83 | foreach (var key in inner.get_members())
84 | if (cb(new Slice.s(key))) break;
85 | }
86 |
87 | public override int to_int(out bool is_length = null) {
88 | is_length = true;
89 | return (int) inner.get_size();
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Services/Prosody/misc/mimeinfo.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** This tag helps Odysseus talk about filetypes in a slightly more natural way. */
18 | namespace Odysseus.Templating.xMIMEInfo {
19 | public class MIMEInfoBuilder : TagBuilder, Object {
20 | public Template? build(Parser parser, WordIter args) throws SyntaxError {
21 | var variables = new Gee.ArrayList();
22 | foreach (var arg in args) variables.add(new Variable(arg));
23 |
24 | return new MIMEInfoTag(variables.to_array());
25 | }
26 | }
27 | private class MIMEInfoTag : Template {
28 | private Variable[] vars;
29 | public MIMEInfoTag(Variable[] variables) {this.vars = variables;}
30 |
31 | public override async void exec(Data.Data ctx, Writer output) {
32 | var mime = new StringBuilder();
33 | foreach (var variable in vars) mime.append(variable.eval(ctx).to_string());
34 |
35 | // I wish I didn't have to, but assume that
36 | // if a filetype is in the database it has a decent description.
37 | if (ContentType.list_registered().index(mime.str) < 0) return;
38 |
39 | var desc = ContentType.get_description(mime.str);
40 | yield output.writes(@"($desc)");
41 | }
42 | }
43 |
44 | public class MIMEIconFilter : Filter {
45 | public override Data.Data filter0(Data.Data mime) {
46 | var ret = ContentType.get_generic_icon_name(@"$mime");
47 | return new Data.Literal(ret == null ? "unknown" : ret);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Services/Prosody/misc/tsv.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Support for reading TSV files into Prosody data.
18 |
19 | This is mostly used for downloading recommendations to fill in gaps in Top Sites. */
20 | namespace Odysseus.Templating.x {
21 | public async Data.Data readTSV(DataInputStream stream) throws IOError {
22 | var rows = new Gee.ArrayList();
23 | for (var line = yield stream.read_line_async(); line != null;
24 | line = yield stream.read_line_async()) {
25 | line = line.strip();
26 | if (line == "" || line[0] == '#') continue;
27 |
28 | var row = line.split("\t");
29 | var prosody_row = new Data.Data[row.length];
30 | for (var i = 0; i < row.length; i++)
31 | prosody_row[i] = new Data.Literal(row[i]);
32 | rows.add(new Data.List.from_array(prosody_row));
33 | }
34 | return new Data.List(rows);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Services/database/completer.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018,2020).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Makes it trivial to write autocompletors as database queries. */
18 | namespace Odysseus.Services {
19 | public abstract class CompleterQuery : Tokenized.CompleterDelegate {
20 | private Sqlite.Statement compiled;
21 | public abstract string sql();
22 |
23 | construct {
24 | if (Database.get_database().prepare_v2(sql(), -1, out compiled) != Sqlite.OK) {
25 | warning("Failed to initialize an autocompleter!\nThe addressbar will not be fully functional.");
26 | compiled = null;
27 | }
28 | }
29 |
30 | public override void autocomplete(string query, Tokenized.Completer c) {
31 | // These completers will generally be unhelpful in this case
32 | if (query == "") return;
33 |
34 | compiled.reset();
35 | compiled.bind_text(1, query);
36 |
37 | while (compiled.step() == Sqlite.ROW) {
38 | c.suggestion(compiled.column_text(1), compiled.column_text(0));
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Services/database/database.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Maintains access to a global database for use by the chrome and internal pages. */
18 | namespace Odysseus.Database {
19 | private Sqlite.Database? main_db;
20 | public unowned Sqlite.Database get_database() {
21 | return main_db;
22 | }
23 |
24 | private class ProsodyPipeSQLite : Templating.Writer, Object {
25 | // NOTE: This Writer doesn't like SQL statements
26 | // being split up by templating.
27 | public async void write(Slice text) {
28 | yield writes(@"$text");
29 | }
30 | public async void writes(string text) {
31 | string err_msg;
32 | var err = get_database().exec(text, null, out err_msg);
33 | if (err != Sqlite.OK)
34 | error("Failed to execute SQL: %s: %s", err_msg, text);
35 | }
36 | }
37 |
38 | public bool setup_database() {
39 | if (main_db != null) return false; // Doesn't need initialization.
40 |
41 | var db_path = Path.build_path(Path.DIR_SEPARATOR_S,
42 | Environment.get_user_config_dir(), "com.github.alcinnz.odysseus", "ui.sqlite");
43 | var err = Sqlite.Database.open(db_path, out main_db);
44 | if (err != Sqlite.OK)
45 | error("Failed to load UI state! " + main_db.errmsg());
46 |
47 | // Register extended tags needed by templates.
48 | // And register standard tags to avoid messing up their autoimport.
49 | Templating.Std.register_standard_library();
50 | Database.Prosody.register_query_tags();
51 |
52 | // Upgrade the database from whatever version it was
53 | var errmsg = "";
54 | int version = 0;
55 | err = main_db.exec("PRAGMA user_version;", (n, values, columns) => {
56 | version = int.parse(values[0]);
57 | var data = Templating.Data.Let.build(
58 | new Slice.s("v"), new Templating.Data.Literal(version));
59 |
60 | var upgrade_path = "/io/github/alcinnz/Odysseus/database/init.sql";
61 | Templating.ErrorData? error_data = null;
62 | Templating.Template template;
63 | try {
64 | template = Templating.get_for_resource(upgrade_path, ref error_data);
65 | } catch (Error err) {
66 | error("Failed to parse init script's templating! %s", err.message);
67 | }
68 |
69 | var writer = new ProsodyPipeSQLite();
70 | var loop = new MainLoop();
71 | template.exec.begin(data, writer, (obj, res) => loop.quit());
72 | loop.run();
73 |
74 | return 0;
75 | }, out errmsg);
76 | if (err != Sqlite.OK)
77 | error("Failed to read UI database version! " + errmsg);
78 | return version == 0;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Services/database/util.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Wraps the SQLite library in functions that are my vala-y */
18 | namespace Odysseus.Database {
19 | /* Adjusts interface to Sqlite.Database.prepare_v2 so that:
20 | 1. It takes a string, not a string slice.
21 | 2. Incorporates error reporting/crashing.
22 | 3. Returns the statement, instead of writing it to an out param. */
23 | public Sqlite.Statement parse(string sql) {
24 | Sqlite.Statement stmt;
25 | var err = get_database().prepare_v2(sql, sql.length, out stmt);
26 | if (err != Sqlite.OK) error("Failed to parse: %s", sql);
27 | return stmt;
28 | }
29 |
30 | public struct QueryIterator {
31 | public unowned Sqlite.Statement stmt;
32 | public Gee.Map? next() {
33 | if (stmt.step() != Sqlite.OK) {
34 | stmt.reset();
35 | return null;
36 | }
37 |
38 | var ret = new Gee.HashMap();
39 | var n_columns = stmt.column_count();
40 | for (var i = 0; i < n_columns; i++) {
41 | ret[stmt.column_name(i)] = stmt.column_value(i);
42 | }
43 | return ret;
44 | }
45 | }
46 | public QueryIterator query(Sqlite.Statement stmt) {
47 | return QueryIterator() {stmt = stmt};
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Services/globals.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Contains code to construct miscellaneous globals, particularly the WebContext
18 | and any configuration folders. */
19 | namespace Odysseus {
20 | private static string build_config_path(string subdir) {
21 | return Path.build_path(Path.DIR_SEPARATOR_S,
22 | Environment.get_user_config_dir(), "com.github.alcinnz.odysseus", subdir);
23 | }
24 |
25 | private static WebKit.WebContext web_ctxt = null;
26 |
27 | public WebKit.WebContext get_web_context() {
28 | if (web_ctxt != null) return web_ctxt;
29 |
30 | var data_manager = Object.@new(typeof(WebKit.WebsiteDataManager),
31 | "base_cache_directory", build_config_path("site-cache"),
32 | "base_data_directory", build_config_path("site-data"),
33 | "disk_cache_directory", build_config_path("http-cache"),
34 | "indexeddb_directory", build_config_path("indexeddb"),
35 | "local_storage_directory", build_config_path("localstorage"),
36 | "offline_application_cache_directory", build_config_path("offline-cache"),
37 | "websql_directory", build_config_path("websql")
38 | ) as WebKit.WebsiteDataManager;
39 | web_ctxt = new WebKit.WebContext.with_website_data_manager(data_manager);
40 |
41 | Traits.setup_context(web_ctxt);
42 | return web_ctxt;
43 | }
44 |
45 | private static Tokenized.CompleterFactory completer;
46 | public Tokenized.CompleterFactory get_main_completers() {
47 | if (completer == null) completer = new Tokenized.CompleterFactory();
48 | return completer;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Services/icons.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | /** Exposes GTK's Icons to WebKit (and in particular internal pages). */
19 | namespace Odysseus.Services {
20 | public void handle_sysicon_uri(WebKit.URISchemeRequest request) {
21 | var stream = new MemoryInputStream();
22 | var components = request.get_path().split("/");
23 | if (components.length != 2) {// Do we need to support fallbacks?
24 | request.finish(stream, 0, "image/png");
25 | return;
26 | }
27 |
28 | var size = int.parse(components[0]);
29 | var icon = components[1];
30 |
31 | uint8[] icon_buffer;
32 | try {
33 | // TODO What flags should I pass? I18n concerns?
34 | var pixbuf = Gtk.IconTheme.get_default().load_icon(icon, size, 0);
35 | pixbuf.save_to_buffer(out icon_buffer, "png");
36 | } catch (Error e) {
37 | request.finish_error(e);
38 | return;
39 | }
40 |
41 | stream.add_data(icon_buffer);
42 | request.finish(stream, icon_buffer.length, "image/png");
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Traits/README:
--------------------------------------------------------------------------------
1 | Various independant behaviours added ontop of webviews.
2 |
3 | These build ontop of Webkit.WebView, the Services modules, and/or possibly WebTab
4 | to add various useful behaviours. They all (with the exception of
5 | navigate/view-source) run some code (through the init.vala module) for every
6 | new WebView and/or WebContext and are not called into in any other way.
7 |
8 | They are essentially plugins with a very limitted but capable API to build on.
9 |
10 | ## Groupings
11 |
12 | These traits are organized by how the extend the webview or other UIs:
13 |
14 |
15 |
autocomplete
16 |
These extend the Completer service rather than a WebView.
17 |
navigate
18 |
These handle page loads via a range of signals on the WebView.
19 |
These may include custom protocols, decide_policy, load_failed, & load_changed.
20 |
decorate
21 |
These respond to other signals on the webview and may trigger changes to the WebTab.
22 |
23 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/bookmarks.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2020).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Allows searching bookmarks by tag. */
18 | namespace Odysseus.Traits {
19 | public class Bookmarks : Tokenized.CompleterDelegate {
20 | private Sqlite.Statement qGetName = Database.parse("SELECT label FROM tags WHERE rowid = ?;");
21 | private Sqlite.Statement qQueryName = Database.parse("SELECT * FROM tag_labels WHERE tag = ? AND altlabel LIKE ?;");
22 | public override void autocomplete(string query, Tokenized.Completer c) {
23 | if (query == null || query == "") return; // Prefer URL autocompletion...
24 |
25 | // Gather tag IDs
26 | var tags = new Gee.ArrayList();
27 | var params_builder = new StringBuilder();
28 | for (var i = 0; i < c.tags.size; i++) {
29 | tags.add(new Database.Tagging.Int64(int64.parse(c.tags[i].val)));
30 | params_builder.append("t=");
31 | params_builder.append(c.tags[i].val);
32 | params_builder.append("&");
33 | }
34 | var param = params_builder.str;
35 |
36 | // Determine related, matching tags.
37 | foreach (var tag in Database.Tagging.related_tags(tags)) {
38 | // query to find label.
39 | qGetName.reset();
40 | qGetName.bind_int64(1, tag.i);
41 | if (qGetName.step() != Sqlite.ROW) continue;
42 | var name = qGetName.column_text(0);
43 | if (name == null) continue;
44 |
45 | // query to see if the name matches query, if provided.
46 | // SELECT * FROM tag_labels WHERE tag = ? AND altlabel = ?;
47 | if (!name.contains(query)) {
48 | qQueryName.reset();
49 | qQueryName.bind_int64(1, tag.i);
50 | qQueryName.bind_text(2, "%" + query + "%");
51 | if (qGetName.step() != Sqlite.ROW) continue;
52 | }
53 | c.suggestion(@"odysseus:bookmarks?$(param)t=$tag", name);
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/ddg-autocomplete.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Loads autocompletions from DuckDuckGo. */
18 | namespace Odysseus.Traits.Search {
19 | public class DDGOnlineCompletions : Tokenized.CompleterDelegate {
20 | private Soup.Session session = new Soup.Session();
21 | private Cancellable in_progress = new Cancellable();
22 | private DuckDuckGo output;
23 |
24 | construct {
25 | output = new DuckDuckGo();
26 | }
27 |
28 | public override void autocomplete(string query, Tokenized.Completer c) {
29 | // Don't want this to be a keylogger, even for DuckDuckGo.
30 | if (!(" " in query)) return;
31 | // Cancel current request if any, then reset for reuse.
32 | in_progress.cancel(); in_progress.reset();
33 |
34 | fetch_completions.begin(query, c);
35 | }
36 |
37 | private async void fetch_completions(string query, Tokenized.Completer c) {
38 | try {
39 | var uri = "https://duckduckgo.com/ac/?q=" + Soup.URI.encode(query, null);
40 | var request = session.request(uri);
41 | var response = yield request.send_async(in_progress);
42 |
43 | // See sample-completion.json for what is being parsed here.
44 | // Streams data in for subtle performance improvements.
45 | var jsonParser = new Json.Parser();
46 | jsonParser.object_member.connect((obj, member) => {
47 | if (member != "phrase") return;
48 | var jsonString = obj.get_string_member(member);
49 | if (jsonString == null) return;
50 |
51 | output.autocomplete(jsonString, c);
52 | });
53 | yield jsonParser.load_from_stream_async(response, in_progress);
54 | } catch (Error e) {
55 | // No biggy.
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/duckduckgo.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Suggests the user "quacks" what they entered into the addressbar. */
18 | namespace Odysseus.Traits.Search {
19 | public class DuckDuckGo : Tokenized.CompleterDelegate {
20 |
21 | public override void autocomplete(string query, Tokenized.Completer c) {
22 | c.suggestion("https://duckduckgo.com/?q=" + Soup.URI.encode(query, null),
23 | "🔍\t" + query);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/history.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Helps out when surfers attempt to revisit a webpage. */
18 | namespace Odysseus.Traits {
19 | public class HistoryAutocompleter : Services.CompleterQuery {
20 | public override string sql() {
21 | return """SELECT title, uri FROM page_visit WHERE INSTR(uri, ?)
22 | ORDER BY length(uri) ASC, visited_at DESC LIMIT 1;""";
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/imply-http.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Prepends 'https://' in the likely case the user dropped it.
18 |
19 | Takes care to leave valid URIs as is so users can take advantage of the fact
20 | URIs entered into the addressbar may be opened by other applications. */
21 | namespace Odysseus.Traits {
22 | public class ImplyHTTP : Tokenized.CompleterDelegate {
23 | public override void autocomplete(string query, Tokenized.Completer c) {
24 | if (" " in query || !("." in query || ":" in query))
25 | return; // Doesn't even resemble a URI!
26 |
27 | // TODO handle IPv6 addresses.
28 | // Maybe other failure cases, but the more sophisticated test broke.
29 | if (":" in query) c.suggestion(query);
30 | else c.suggestion("https://" + query);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/redpanda.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2020).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Easter egg to test the tokenized entry. */
18 | namespace Odysseus.Traits {
19 | public class RedPanda : Tokenized.CompleterDelegate {
20 | static string[] heros = {
21 | "Red Panda", "Flying Squirrel", "Stranger",
22 | "Tom Tomorrow, Man Of The Future",
23 | "Brian McSweeny, Man Of A Thousand Faces",
24 | "Red Squirrel", "Captain Tom Sunlight", "Red Ensen",
25 | "Justice Union", "Lady Luck", "Danger Dame", "Ogre",
26 | "Home Team", "Doc Rocket", "Molecule Max",
27 | "Danger Federation", "Eagle Smith", "Jenny Swift", "Titanic Man",
28 | "Blue Bomber", "Doctor Improbable", "Mystic", "White Knight",
29 | "Star Lass", "Grey Fox", "Mr Amazing",
30 | "Black Eagle"
31 | };
32 |
33 | public override void autocomplete(string query, Tokenized.Completer c) {
34 | var builder = new StringBuilder();
35 | int index = 0;
36 | unichar ch;
37 | bool capitalize = true;
38 |
39 | while (query.get_next_char(ref index, out ch)) {
40 | if (capitalize) {
41 | ch = ch.totitle();
42 | capitalize = false;
43 | } else {
44 | ch = ch.tolower();
45 | capitalize = ch.isspace();
46 | }
47 | builder.append_unichar(ch);
48 | }
49 |
50 | if (builder.str in heros) c.token(builder.str, "The Red Panda demands it!");
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Traits/autocomplete/sample-completion.json:
--------------------------------------------------------------------------------
1 | [{"phrase":"docs"},{"phrase":"docusign"},{"phrase":"doctor who"},{"phrase":"doc mcstuffins"},{"phrase":"doc holliday"},{"phrase":"doc martin"},{"phrase":"doctoroz.com"},{"phrase":"doctor strange"},{"phrase":"doc martens"},{"phrase":"documentary"}]
2 |
--------------------------------------------------------------------------------
/src/Traits/decorate/AutoScroll.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /* Scroll by moving the dragging with a middle-click.
18 |
19 | This is a great help for reading long webpages (say, the upcoming odysseus:history),
20 | and is a common but not ubiquitous feature of other browsers. */
21 | namespace Odysseus.Traits {
22 | public void setup_autoscroll(WebKit.WebView web) {
23 | double start_x = 0, start_y = 0, x = 0, y = 0;
24 | bool autoscroll_active = false;
25 |
26 | web.button_press_event.connect((evt) => {
27 | if (evt.button != 2 || autoscroll_active) return false;
28 |
29 | web.get_window().set_cursor(new Gdk.Cursor.from_name(Gdk.Display.get_default(), "move"));
30 |
31 | x = start_x = evt.x; y = start_y = evt.y;
32 | autoscroll_active = true;
33 |
34 | Timeout.add(100 /* 10 times per second*/, () => {
35 | // It's scroll using JavaScript than in Vala here…
36 | var js = "window.scrollBy(%f, %f)".printf(x - start_x, y - start_y);
37 | web.run_javascript.begin(js, null);
38 | return autoscroll_active;
39 | });
40 | return false;
41 | });
42 |
43 | web.motion_notify_event.connect((evt) => {
44 | if (!autoscroll_active) return false;
45 |
46 | x = evt.x; y = evt.y;
47 | return true;
48 | });
49 |
50 | web.button_release_event.connect((evt) => {
51 | if (!autoscroll_active) return false;
52 |
53 | web.get_window().set_cursor(null);
54 | autoscroll_active = false;
55 | return false;
56 | });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Traits/decorate/addressbar-autofocus.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Simplifies user interaction when opening a new tab.
18 | Because normally opening a new tab indicates they want to go somewhere
19 | specific. */
20 | namespace Odysseus.Traits {
21 | public void setup_addressbar_autofocus(WebTab tab) {
22 | var web = tab.web;
23 | web.load_changed.connect((evt) => {
24 | if (evt != WebKit.LoadEvent.STARTED || web.uri != "odysseus:home") return;
25 | var win = tab.get_toplevel() as BrowserWindow;
26 | if (win == null) return;
27 |
28 | win.addressbar.entry.grab_focus();
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Traits/decorate/alert.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** This moderately improves the UI for JavaScript alerts/confirms/prompts.
18 |
19 | I know this isn't the best UI but take up with webpages which use these APIs,
20 | I'm just trying to make it not quite so bad by reducing it's modality. */
21 | namespace Odysseus.Traits {
22 | private async bool show_alert(WebTab tab, WebKit.ScriptDialog dlg,
23 | out bool confirm, out bool prompt) {
24 | var msg = dlg.get_message();
25 | var opts = new Overlay.InfoContainer.MessageOptions();
26 | switch (dlg.get_dialog_type()) {
27 | case WebKit.ScriptDialogType.ALERT:
28 | opts.type = Gtk.MessageType.INFO; opts.show_cancel = false;
29 | confirm = false; prompt = false;
30 | break;
31 | case WebKit.ScriptDialogType.CONFIRM:
32 | confirm = true; prompt = false;
33 | break;
34 | case WebKit.ScriptDialogType.PROMPT:
35 | opts.show_entry = true;
36 | opts.prefill = dlg.prompt_get_default_text();
37 | confirm = false; prompt = true;
38 | break;
39 | case WebKit.ScriptDialogType.BEFORE_UNLOAD_CONFIRM:
40 | opts.ok_text = _("Leave"); opts.cancel_text = _("Stay");
41 | confirm = true; prompt = false;
42 | break;
43 | default:
44 | error("Unreachable code");
45 | }
46 |
47 | return yield tab.info.message(msg, opts);
48 | }
49 |
50 | public void setup_alerts(WebTab tab) {
51 | tab.web.script_dialog.connect((dlg) => {
52 | var loop = new MainLoop();
53 | bool ret = true;
54 | bool confirm = true; bool prompt = false;
55 |
56 | // Disallow interacting with the page while dialog is active.
57 | tab.web.sensitive = false;
58 | tab.status = _("Close the message to continue interacting with this page");
59 |
60 | show_alert.begin(tab, dlg, (obj, res) => {
61 | ret = show_alert.end(res, out confirm, out prompt);
62 | loop.quit();
63 | });
64 | loop.run();
65 |
66 | // Dialog disappeared, reenable page.
67 | tab.web.sensitive = true;
68 | tab.status = tab.default_status;
69 |
70 | if (confirm) dlg.confirm_set_confirmed(ret);
71 | if (prompt && ret) dlg.prompt_set_text(tab.info.response);
72 | return true;
73 | });
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Traits/decorate/fix-bg-videos.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Bandaid fix for WebKitGTK WebViews not cleaning GStreamer threads
18 | up after themselves.
19 |
20 | The fix here is just to pause videos on tab close.
21 | Needs to be given it's own mainloop so the JS runs before
22 | the tab actually closes.
23 |
24 | Unfortunately this requires events on the DynamicNotebook for proper behaviour,
25 | so it needs to be called in from the UI code rather than
26 | with the other traits. */
27 | namespace Odysseus.Traits {
28 | public void pause_bg_videos(Granite.Widgets.DynamicNotebook tabs) {
29 | var js = "for (let media of document.querySelectorAll('video, audio')) media.pause()";
30 | tabs.tab_removed.connect((tab) => {
31 | (tab as WebTab).web.run_javascript.begin(js, null);
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Traits/decorate/fix-plus.google.com.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /* Fix for the blurriness of Google Plus's fonts.
18 |
19 | Not really something I want to build into Odysseus as I'd rather focus it should
20 | focus on making the whole web better, not just specific sites. Besides
21 | this only covers the symptoms, and not the real issue. */
22 | namespace Odysseus.Traits {
23 | public void fix_google_plus(WebKit.WebView web) {
24 | var css = new WebKit.UserStyleSheet("* {-webkit-font-smoothing: subpixel-antialiased;}",
25 | WebKit.UserContentInjectedFrames.ALL_FRAMES,
26 | WebKit.UserStyleLevel.USER,
27 | new string[] {"*"}, new string[0]);
28 | web.user_content_manager.add_style_sheet(css);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Traits/decorate/internal-favicons.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /* Detects when a page is internal, and uses special syntax embedded in the
18 | page's title to determine it's favicon as WebKit refuses to give us the
19 | correct favicon. */
20 | namespace Odysseus.Traits {
21 | /* Makes it easier to put symbolic icons in tabbar & non-symbolic in addressbar. */
22 | errordomain Availability {UNAVAILABLE}
23 | private Icon choose_icon(string spec, string suffix = "") throws Error {
24 | var theme = Gtk.IconTheme.get_default();
25 |
26 | var icons = spec.split_set(" \t\r\n");
27 | for (var i = 0; i < icons.length; i++) icons[i] = icons[i] + suffix;
28 |
29 | var icon = theme.choose_icon(icons, 16, Gtk.IconLookupFlags.GENERIC_FALLBACK);
30 | if (icon == null) throw new Availability.UNAVAILABLE("");
31 |
32 | return icon.load_icon();
33 | }
34 |
35 | private void setup_internal_favicons(WebTab tab) {
36 | var web = tab.web;
37 | web.notify["title"].connect((pspec) => {
38 | var title = tab.web.title.chug();
39 | if (title[0] != '[') return;
40 |
41 | // Extract icon from title & set it on the tab
42 | var splitat = title.index_of_char(']');
43 | if (splitat < 0) return;
44 | var icons = title[1:splitat];
45 |
46 | try {
47 | var colour = Gdk.RGBA();
48 | colour.parse("#666");
49 | tab.icon = choose_icon(icons, "-symbolic");
50 | tab.coloured_icon = choose_icon(icons);
51 | tab.label = title[splitat+1:title.length].strip();
52 | } catch (Error err) {}
53 | });
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Traits/decorate/notifications.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | /** Shows notifications using a newer API than WebKit does. */
19 | namespace Odysseus.Traits {
20 | public bool show_notification(WebKit.Notification notif) {
21 | var sys = new Notification(notif.title);
22 | sys.set_body(notif.body);
23 | Odysseus.Application.instance.send_notification(null, sys);
24 | notif.close();
25 |
26 | return true;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Traits/decorate/permit.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Exposes a UI for when WebKit asks for various permissions
18 | on behalf of webpages.
19 |
20 | This is not the final UI as it neither persists configurations
21 | nor allows the user to edit the persisted permissions.
22 | Also It's an easy UI for a webpage to spoof,
23 | so an alternative would be more secure.
24 | As such this UI will be reimplemented later. */
25 | namespace Odysseus.Traits {
26 | private async bool confirm_permit(WebTab tab, WebKit.PermissionRequest req) {
27 | var msg = "";
28 | var opts = new Overlay.InfoContainer.MessageOptions();
29 | opts.type = Gtk.MessageType.WARNING;
30 | opts.ok_text = _("Allow"); opts.cancel_text = _("Deny");
31 | var icon = "";
32 |
33 | if (req is WebKit.GeolocationPermissionRequest) {
34 | msg = _("This page wants to know where you currently are.");
35 | icon = "find-location"; // FIXME non-standard.
36 | } else if (req is WebKit.NotificationPermissionRequest) {
37 | msg = _("This page wants the ability to show you notifications.");
38 | icon = "notification"; // FIXME non-standard.
39 | } else if (req is WebKit.UserMediaPermissionRequest) {
40 | var media_req = req as WebKit.UserMediaPermissionRequest;
41 | var listens = media_req.is_for_audio_device;
42 | var watches = media_req.is_for_video_device;
43 | if (listens && watches) {
44 | msg = _("This page wants to watch and listen to you.");
45 | icon = "camera-photo";
46 | } else if (listens) {
47 | msg = _("This page wants to listen to you.");
48 | icon = "audio-input-microphone";
49 | } else if (watches) {
50 | msg = _("This page wants to watch you.");
51 | icon = "camera-video";
52 | } else return false;
53 | } else if (req is WebKit.InstallMissingMediaPluginsPermissionRequest) {
54 | icon = "media-icon-start";
55 | var install_req = req as
56 | WebKit.InstallMissingMediaPluginsPermissionRequest;
57 | msg = _("Additional software is required to play media on this page:");
58 | msg += "\n" + install_req.get_description();
59 | opts.ok_text = _("Install");
60 | }
61 |
62 | // FIXME Should be disabled and turn active.
63 | tab.indicators.add(new StatusIndicator(icon, Status.ACTIVE, msg));
64 | tab.indicators_loaded(tab.indicators);
65 | return yield tab.info.message(msg, opts);
66 | }
67 |
68 | public void setup_permits(WebTab tab) {
69 | tab.web.permission_request.connect((req) => {
70 | confirm_permit.begin(tab, req, (obj, res) => {
71 | if (confirm_permit.end(res)) req.allow();
72 | else req.deny();
73 | });
74 | return true;
75 | });
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Traits/download-progress.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | /** Integrates downloads into the Pantheon desktop.
19 |
20 | Specifially it renders download progress onto the app icon
21 | and it notifies users of download completion. */
22 | namespace Odysseus.Traits {
23 | public void show_download_progress_on_icon(Download dl) {
24 | var appuri = "application://com.github.alcinnz.odysseus.desktop";
25 | var launcher = new LauncherEntry();
26 |
27 | try {
28 | var conn = Bus.get_sync(BusType.SESSION);
29 | conn.register_object(@"/com/canonical/unity/launcherentry/$(appuri.hash())", launcher);
30 | } catch (IOError e) {
31 | /* Ignore, assuming the desktop doesn't support these indicators. */
32 | }
33 |
34 | dl.received_data.connect(() => {
35 | Idle.add(() => {
36 | var props = new HashTable(str_hash, str_equal);
37 |
38 | var downloads = DownloadSet.get_downloads().downloads;
39 | if (downloads.size == 0) {
40 | props.insert("progress-visible", false);
41 | if (launcher != null) launcher.update(appuri, props);
42 | return false;
43 | }
44 |
45 | var progress = 1.0;
46 | foreach (var download in downloads)
47 | progress *= download.download.estimated_progress;
48 |
49 | props.insert("progress-visible", true);
50 | props.insert("progress", progress);
51 | if (launcher != null) launcher.update(appuri, props);
52 |
53 | return false;
54 | }, Priority.LOW);
55 | });
56 | dl.finished.connect(() => {
57 | if (dl.cancelled) return;
58 |
59 | var notify = new Notification(_("Web Download Completed"));
60 | var response = dl.download.response;
61 | var url = new Soup.URI(response.uri);
62 | notify.set_body(url.path[url.path.last_index_of("/")+1:url.path.length] + "\n" + url.host);
63 | notify.set_icon(dl.icon);
64 | Odysseus.Application.instance.send_notification(null, notify);
65 | });
66 | }
67 |
68 | [DBus(name="com.canonical.Unity.LauncherEntry")]
69 | private class LauncherEntry : Object {
70 | public signal void update(string uri, HashTable properties);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Traits/download-window.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Without this trait, it is too easy for a surfer to accidentally
18 | cancel all downloads by closing Odysseus. This trait adds a new
19 | toplevel and minimized window to address this problem whilst
20 | ensuring the download progress stays in the dock for a quick glance. */
21 | /* NOTE: This builds on the UI layer to expose it's own. */
22 | namespace Odysseus.Traits {
23 | private class DownloadsWindow : Gtk.ApplicationWindow {
24 | public static DownloadsWindow? instance = null;
25 |
26 | construct {
27 | title = _("Downloads Are In Progress…");
28 | icon_name = "browser-download";
29 | deletable = false;
30 |
31 | var header = new Gtk.HeaderBar();
32 | header.title = title;
33 | header.has_subtitle = true;
34 | header.subtitle = _("Click a download to cancel or alter it");
35 | // The whole point of this window is to be open while downloads are
36 | // in progress, so replace normal close buttons with minimize.
37 | header.show_close_button = false;
38 | var min = new Gtk.Button.from_icon_name("window-minimize-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
39 | min.clicked.connect(() => iconify());
40 | header.pack_start(min);
41 | var restore = new Gtk.Button.from_icon_name("document-open-recent-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
42 | restore.clicked.connect(() => Persist.restore_application());
43 | header.pack_end(restore);
44 | set_titlebar(header);
45 |
46 | // For window content, a Downloads bar is appropriate.
47 | // However the X instead of closing it should simply clear away
48 | // completed downloads
49 | var body = new DownloadsBar();
50 | body.notify["child-revealed"].connect((pspec) => body.reveal_child = true);
51 | body.reveal_child = true;
52 | add(body);
53 | }
54 | }
55 |
56 | private void consider_show_download_window() {
57 | Idle.add(() => {
58 | if (DownloadSet.get_downloads().downloads.size > 0) {
59 | if (DownloadsWindow.instance != null) return false;
60 |
61 | DownloadsWindow.instance = new DownloadsWindow();
62 | DownloadsWindow.instance.show_all();
63 | DownloadsWindow.instance.iconify();
64 | } else {
65 | DownloadsWindow.instance.destroy();
66 | DownloadsWindow.instance = null;
67 | }
68 | return false;
69 | }, Priority.LOW);
70 | }
71 |
72 | public void download_window_handle_download(Download dl) {
73 | dl.finished.connect(consider_show_download_window);
74 | consider_show_download_window();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Traits/navigate/autodownload.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | namespace Odysseus.Traits {
19 | public void setup_autodownload(WebTab tab) {
20 | var web = tab.web;
21 |
22 | web.decide_policy.connect((decision, type) => {
23 | if (type == WebKit.PolicyDecisionType.RESPONSE) {
24 | var response_decision = (WebKit.ResponsePolicyDecision) decision;
25 | var mime_type = Download.normalize_mimetype(response_decision.response);
26 |
27 | if (!response_decision.is_mime_type_supported() ||
28 | /* Show videos in Audience */
29 | mime_type.has_prefix("video/") || mime_type == "application/ogg") {
30 | // TODO This commented would be a nice experience, if only it
31 | // worked well in combination with cookies.
32 | // (I tried, and ended up with libSoup being unwilling to synchronize)
33 | /*var appinfo = AppInfo.get_default_for_type(mime_type, false);
34 | if (appinfo.supports_uris()) {
35 | // Probably means it supports HTTP URIs.
36 | var uris = new List();
37 | uris.append(response_decision.response.uri);
38 | try {
39 | appinfo.launch_uris(uris, null);
40 | decision.ignore();
41 | return true;
42 | } catch (Error e) {
43 | // Fallback to download
44 | }
45 | }*/
46 |
47 | // Didn't work, download it first.
48 | decision.download();
49 | decision.ignore();
50 | return true;
51 | }
52 | }
53 | return false;
54 | });
55 |
56 | web.load_failed.connect((load_evt, uri, err) => {
57 | if (!err.matches(WebKit.PolicyError.quark(), 101)) return false;
58 |
59 | var schema = uri.split(":", 2)[0];
60 | var app = AppInfo.get_default_for_uri_scheme(schema);
61 | if (app == null) {
62 | report_error("schema", uri, tab);
63 | } else if (app.get_id() == Odysseus.Application.instance.application_id) {
64 | // NOTE: If this case isn't handled, there'd be an infinite loop
65 | // between this and Odysseus.Application.open.
66 | report_error("url", uri, tab);
67 | } else {
68 | var uris = new List();
69 | uris.append(uri);
70 | try {
71 | app.launch_uris(uris, null);
72 | } catch (Error err) {
73 | warning(err.message);
74 | }
75 | }
76 | return true;
77 | });
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Traits/navigate/harvest-recommendations.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2019).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Extracts unvisited links from webpages in order to recommended them later. */
18 | namespace Odysseus.Traits {
19 | public void setup_recommendations_harvester(WebTab tab) {
20 | var qCheckLinks = Database.parse("SELECT rowid FROM unvisited_links WHERE uri = ?;");
21 | var qCheckHistory = Database.parse("SELECT * FROM page_visit WHERE uri = ?;");
22 | var qUpdateLink = Database.parse("UPDATE unvisited_links SET endorsements = endorsements + ? WHERE uri = ?;");
23 | var qInsertLink = Database.parse("INSERT INTO unvisited_links(endorsements, uri) VALUES (?, ?);");
24 | var qCheckSources = Database.parse("SELECT * FROM link_sources WHERE link = ? AND domain = ?;");
25 | var qInsertSource = Database.parse("INSERT INTO link_sources(link, domain) VALUES (?, ?);");
26 |
27 | tab.links_parsed.connect((links, _) => {
28 | foreach (var link in links) {
29 | if (link.tag != "a" || link.href.has_prefix("odysseus:")) continue;
30 | qCheckHistory.bind_text(1, link.href);
31 | if (!testQuery(qCheckHistory)) continue;
32 |
33 | // Now we know we want to save this link.
34 | var domain = new Soup.URI(tab.url).host;
35 |
36 | // Save the link itself.
37 | qCheckLinks.bind_text(1, link.href);
38 | var isUpdating = testQuery(qCheckLinks);
39 | unowned Sqlite.Statement query = isUpdating ? qUpdateLink : qInsertLink;
40 |
41 | query.reset();
42 | // Ofcourse sites recommend themselves, it means more when they recommend others.
43 | query.bind_int(1, domain == new Soup.URI(link.href).host ? 1 : 10);
44 | query.bind_text(2, link.href);
45 | if (query.step() != Sqlite.OK) continue;
46 | var linkid = isUpdating ?
47 | qCheckLinks.column_int64(0) : Database.get_database().last_insert_rowid();
48 |
49 | // Save an attribution
50 | qCheckSources.bind_int64(1, linkid);
51 | qCheckSources.bind_text(2, domain);
52 | if (!testQuery(qCheckSources)) {
53 | qInsertSource.reset();
54 | qInsertSource.bind_int64(1, linkid);
55 | qInsertSource.bind_text(2, domain);
56 | qInsertSource.step();
57 | }
58 | }
59 | });
60 | }
61 |
62 | private bool testQuery(Sqlite.Statement query) {
63 | query.reset();
64 | return query.step() == Sqlite.ROW;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Traits/navigate/newtab.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | /** Handles certain modifiers to mean the link should be opened in a new tab. */
19 | namespace Odysseus.Traits {
20 | public void setup_newtab_shortcuts(WebKit.WebView web) {
21 | web.decide_policy.connect((decision, type) => {
22 | if (type != WebKit.PolicyDecisionType.NAVIGATION_ACTION) return false;
23 |
24 | var nav = (decision as WebKit.NavigationPolicyDecision).navigation_action;
25 | var use_new = (Gdk.ModifierType.CONTROL_MASK & nav.get_modifiers()) != 0
26 | || nav.get_mouse_button() == 2;
27 | if (!use_new) return false;
28 |
29 | decision.ignore();
30 | web.create(nav);
31 | return true;
32 | });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Traits/navigate/other-types.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Renders additional MIMEtypes to HTML.
18 | Of particular interest are the MIMEtypes Odysseus has some specific knowledge
19 | of otherwise. */
20 | namespace Odysseus.Traits {
21 | using Templating;
22 |
23 | public void setup_other_mimetypes(WebTab tab) {
24 | tab.web.decide_policy.connect((decision, type) => {
25 | if (type != WebKit.PolicyDecisionType.RESPONSE) return false;
26 | var response = decision as WebKit.ResponsePolicyDecision;
27 |
28 | var mime = response.response.mime_type;
29 | var supports_mime = false;
30 | try {
31 | var path = @"/io/github/alcinnz/Odysseus/odysseus:/viewers/$mime";
32 | supports_mime = resources_get_info(path, 0, null, null);
33 | } catch (Error err) {supports_mime = false;}
34 |
35 | var query = Data.Let.builds("url", new Data.Literal(tab.web.uri),
36 | new Data.Literal("url=" + Soup.URI.encode(tab.web.uri, null)));
37 | var data = Data.Let.builds("url", Data.Let.builds("query", query),
38 | new Data.Literal("odysseus:view"));
39 |
40 | if (supports_mime) Services.render_alternate_html.begin(tab, "view",
41 | null, true, data);
42 |
43 | return supports_mime;
44 | });
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Traits/navigate/persist-tab-history.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Persists tab history in that tab's restore_data. */
18 | namespace Odysseus.Traits {
19 | public void setup_persist_tab_history(WebTab tab) {
20 | var web = tab.web;
21 |
22 | web.load_changed.connect((evt) => {
23 | var history = web.get_back_forward_list();
24 | if (history.get_current_item() == null) return;
25 | var json = new Json.Builder();
26 |
27 | json.begin_object();
28 | json.set_member_name("current");
29 | json.add_string_value(history.get_current_item().get_uri());
30 | json.set_member_name("title");
31 | json.add_string_value(history.get_current_item().get_title());
32 | json.set_member_name("back");
33 | json.begin_array();
34 | foreach (var record in history.get_back_list()) {
35 | json.begin_object();
36 | json.set_member_name("href");
37 | json.add_string_value(record.get_uri());
38 | json.set_member_name("title");
39 | json.add_string_value(record.get_title());
40 | json.end_object();
41 | }
42 | json.end_array();
43 | json.set_member_name("forward");
44 | json.begin_array();
45 | foreach (var record in history.get_forward_list()) {
46 | json.begin_object();
47 | json.set_member_name("href");
48 | json.add_string_value(record.get_uri());
49 | json.set_member_name("title");
50 | json.add_string_value(record.get_title());
51 | json.end_object();
52 | }
53 | json.end_array();
54 | json.end_object();
55 |
56 | var generator = new Json.Generator();
57 | generator.set_root(json.get_root());
58 | tab.restore_data = generator.to_data(null);
59 | Persist.save_restore_data(tab);
60 | });
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Traits/navigate/spring-clean.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2019).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | namespace Odysseus.Traits {
19 | private short count = 0;
20 | public void maybe_spring_clean(WebKit.LoadEvent event) {
21 | if (event != WebKit.LoadEvent.FINISHED || count++ != 16) return;
22 |
23 | Templating.ErrorData errData = null;
24 | try {
25 | Templating.get_for_resource("/io/github/alcinnz/Odysseus/odysseus:/spring-clean", ref errData)
26 | .exec.begin(new Templating.Data.Empty(), new Templating.VoidWriter());
27 | } catch (Error err) {warning("Failed to run spring cleaning.");}
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Traits/navigate/viewsource.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 |
18 | using Odysseus.Services;
19 | using Odysseus.Templating.Data;
20 | namespace Odysseus.Traits {
21 | using Templating;
22 | public async string view_source(WebKit.WebView source) {
23 | var data = new Source();
24 | data.title = source.title;
25 | try {
26 | var code = yield source.get_main_resource().get_data(null);
27 | data.code = new Slice.a(code);
28 | } catch (Error e) {
29 | return "odysseus:errors/no-source"; // Don't go through
30 | }
31 |
32 | var url = "source:///" + source.get_main_resource().uri;
33 | if (sources == null) sources = new Gee.HashMap();
34 | sources[url] = data;
35 | return url;
36 | }
37 |
38 | private class Source {
39 | public string title;
40 | public Slice code;
41 | }
42 | private Gee.Map? sources = null; // UGLY HACK
43 |
44 | public void handle_source_uri(WebKit.URISchemeRequest request) {
45 | if (sources != null && sources.has_key(request.get_uri())) {
46 | var resource = sources[request.get_uri()];
47 | sources.unset(request.get_uri());
48 |
49 | var url = request.get_uri();
50 | var data = Let.builds("source", new Substr(resource.code),
51 | Let.builds("title", new Literal(resource.title),
52 | Let.builds("url", new Literal(url["source:///".length:url.length]))));
53 |
54 | try {
55 | ErrorData? ignored = null;
56 | var template = get_for_resource(
57 | "/io/github/alcinnz/Odysseus/odysseus:/special/viewsource",
58 | ref ignored);
59 | // This is the reason for the hack: InputStreamWriter
60 | var stream = new InputStreamWriter();
61 | request.finish(stream, -1, "text/html");
62 | template.exec.begin(data, stream, (obj, res) => stream.close_write());
63 | } catch (Error err) {
64 | // Don't bother reporting errors better
65 | request.finish_error(err);
66 | }
67 | } else {
68 | // If we're not viewing alternate HTML under this schema,
69 | // close any tabs that have persisted.
70 | var response = _("Please go through the \"View Source\" menu item.");
71 | var stream = new MemoryInputStream.from_bytes(new Slice.s(response)._);
72 | request.finish(stream, response.length, "text/html");
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Traits/status/local.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Indicates local resources as being "super-secure" */
18 | namespace Odysseus.Traits {
19 | public void report_local(Gee.List indicators, WebKit.WebView web) {
20 | var local_prefixes = ("odysseus source icon about file " +
21 | "https://127.0.0.1 http://127.0.0.1 https://localhost http://localhost"
22 | ).split(" ");
23 | var is_local = false;
24 | foreach (var prefix in local_prefixes) {
25 | var uri = web.uri;
26 | is_local = uri.has_prefix(prefix + ":") || uri.has_prefix(prefix + "/");
27 | if (is_local) break;
28 | }
29 | if (!is_local) return;
30 |
31 | indicators.add(new StatusIndicator("view-private computer", Status.SECURE,
32 | _("This page was loaded from your computer. Noone else knows you're here.")
33 | ));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Widgets/Chromeless.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2016-2018).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** Improved window for WebInspector, which doesn't duplicate the headerbar. */
18 | public class Odysseus.Chromeless : Gtk.Window {
19 | public Chromeless(WebKit.WebInspector inspector) {
20 | var titlebar = new Gtk.HeaderBar();
21 | set_titlebar(titlebar);
22 | titlebar.no_show_all = true;
23 |
24 | title = _("Web Inspector") + " — " + inspector.inspected_uri;
25 | add(inspector.get_web_view());
26 |
27 | inspector.closed.connect(() => this.destroy());
28 | inspector.attach.connect(() => {
29 | remove(inspector.get_web_view());
30 | this.destroy();
31 | return false;
32 | });
33 |
34 | inspector.get_web_view().button_press_event.connect ((e) => {
35 | if (e.type == Gdk.EventType.@2BUTTON_PRESS && e.button == Gdk.BUTTON_PRIMARY) {
36 | begin_move_drag ((int) e.button, (int) e.x_root, (int) e.y_root, e.time);
37 | return true;
38 | }
39 | return false;
40 | });
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Widgets/DownloadBar.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2016).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | public class Odysseus.DownloadsBar : Gtk.Revealer {
18 | private Gtk.FlowBox mainbox;
19 | private Gtk.Grid box;
20 |
21 | public DownloadsBar() {
22 | box = new Gtk.Grid();
23 | box.orientation = Gtk.Orientation.HORIZONTAL;
24 | box.column_spacing = 5;
25 | this.add(box);
26 |
27 | mainbox = new Gtk.FlowBox();
28 | mainbox.margin = 5;
29 | mainbox.column_spacing = 10;
30 | mainbox.row_spacing = 10;
31 | mainbox.expand = true;
32 | box.add(mainbox);
33 |
34 | add_action("folder", _("View all downloads"),
35 | (button) => {
36 | try {
37 | AppInfo.launch_default_for_uri(Download.folder.get_uri(), null);
38 | } catch (Error e) {
39 | button.image = new Gtk.Image.from_icon_name("error", Gtk.IconSize.MENU);
40 | warning("Failed to open file manager: %s", e.message);
41 | }
42 | });
43 | add_action("window-close", _("Close downloads bar"), (_) => set_reveal_child(false));
44 |
45 | reveal_child = false;
46 | notify["child-revealed"].connect((pspec) => {
47 | if (child_revealed) populate_downloads();
48 | else {
49 | mainbox.@foreach((widget) => widget.destroy());
50 | DownloadSet.get_downloads().disconnect(on_add);
51 | }
52 | });
53 | DownloadSet.get_downloads().add.connect((dl) => reveal_child = true);
54 | }
55 |
56 | private delegate void Action(Gtk.Button button);
57 | private void add_action(string icon, string help, owned Action action) {
58 | var button = new Gtk.Button.from_icon_name(icon);
59 | button.clicked.connect(() => action(button));
60 | button.relief = Gtk.ReliefStyle.NONE;
61 | button.tooltip_text = help;
62 | box.add(button);
63 | }
64 |
65 | public void add_download(Odysseus.Download dl) {
66 | mainbox.add(new DownloadButton(dl));
67 | }
68 |
69 | private ulong on_add = 0;
70 | public void populate_downloads() {
71 | foreach (var download in DownloadSet.get_downloads().downloads) {
72 | add_download(download);
73 | }
74 | on_add = DownloadSet.get_downloads().add.connect((download) => {
75 | add_download(download);
76 | });
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Widgets/ProgressBin.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2016).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | public class Odysseus.ProgressBin : Gtk.Bin {
18 | public Cairo.Pattern progressFill {get; set;}
19 | public double progress {get; set;}
20 |
21 | public ProgressBin() {
22 | var c = Gdk.RGBA();
23 | c.parse("#8cd5ff");
24 | progressFill = new Cairo.Pattern.rgba(c.red, c.green, c.blue, 0.8);
25 |
26 | this.notify.connect((sender, property) => queue_draw());
27 |
28 | // Add some a11y
29 | set_accessible_role(Atk.Role.PROGRESS_BAR);
30 | set_accessible_type(typeof(ProgressBinA11y));
31 | }
32 |
33 | public override bool draw(Cairo.Context cr) {
34 | var width = get_allocated_width();
35 | var height = get_allocated_height();
36 |
37 | // Render progress
38 | cr.rectangle(0, 0, width * progress, height);
39 | cr.set_source(progressFill);
40 | cr.fill();
41 |
42 | // but below the content
43 | get_child().draw(cr);
44 |
45 | return true;
46 | }
47 | }
48 |
49 | public class Odysseus.ProgressBinA11y : Gtk.ContainerAccessible {
50 | construct {
51 | var pbin = widget as ProgressBin;
52 | pbin.notify["progress"].connect((sender, prop) =>
53 | accessible_value = pbin.progress);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Widgets/header/AddressBar2.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2016-2020).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | public class Odysseus.Header.AddressBar : TokenizedEntry {
18 | private Gee.List statusbuttons = new Gee.ArrayList();
19 |
20 | private Sqlite.Statement qGetName = Database.parse("SELECT label FROM tags WHERE rowid = ?;");
21 | public string text {
22 | get {return entry.text;}
23 | set {
24 | clear_tokens();
25 | if (value.has_prefix("odysseus:bookmarks?")) {
26 | var query = value.split("?", 2)[1].split("&");
27 | foreach (var param in query) {
28 | if (!param.has_prefix("t=")) continue;
29 | var tag = param[2:param.length];
30 | if (tag == "") continue;
31 |
32 | // Query to get the label
33 | qGetName.reset();
34 | qGetName.bind_int64(1, int64.parse(tag));
35 | if (qGetName.step() != Sqlite.ROW) continue;
36 | var name = qGetName.column_text(0);
37 | if (name == null) continue;
38 |
39 | addtoken_raw(name, tag);
40 | }
41 | entry.text = "";
42 | } else entry.text = value;
43 | // TODO render t parameters as tokens...
44 | }
45 | }
46 |
47 | construct {
48 | this.autocompleter = get_main_completers().build();
49 |
50 | this.entry.focus_in_event.connect((evt) => {
51 | Idle.add(() => {
52 | entry.select_region(0, -1);
53 | return false;
54 | }, Priority.HIGH); // To aid retyping URLs, copy+paste
55 | return false;
56 | });
57 | }
58 |
59 | public void show_indicators(Gee.List indicators) {
60 | foreach (var widget in statusbuttons) widget.destroy();
61 | statusbuttons.clear();
62 |
63 | foreach (var indicator in indicators) {
64 | var statusbutton = indicator.build_ui();
65 | this.add(statusbutton);
66 | statusbuttons.add(statusbutton);
67 | }
68 | show_all();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Widgets/overlays/InfoContainer.vala:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Odysseus Web Browser (Copyright Adrian Cochrane 2017).
3 | *
4 | * Odysseus is free software: you can redistribute it and/or modify
5 | * it under the terms of the GNU General Public License as published by
6 | * the Free Software Foundation, either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * Odysseus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 |
14 | * You should have received a copy of the GNU General Public License
15 | * along with Odysseus. If not, see .
16 | */
17 | /** InfoContainer is a wrapper around Gtk.InfoBar which shows and hides it
18 | above some other content.
19 |
20 | Used by select traits (Alert & Permit). */
21 | public class Odysseus.Overlay.InfoContainer : Gtk.Grid {
22 | private Gtk.InfoBar info;
23 | private Gtk.Revealer revealer;
24 | private Gtk.Label body;
25 | private Gtk.Entry entry;
26 | private weak Gtk.Button ok_button;
27 | private weak Gtk.Button cancel_button;
28 |
29 | public string response {
30 | get {return entry.text;}
31 | set {entry.text = value;}
32 | }
33 |
34 | construct {
35 | this.body = new Gtk.Label(null);
36 | this.body.wrap = true;
37 |
38 | this.entry = new Gtk.Entry();
39 |
40 | var container = new Gtk.FlowBox();
41 | container.add(this.body);
42 | container.add(this.entry);
43 |
44 | this.info = new Gtk.InfoBar();
45 | this.info.get_content_area().add(container);
46 | this.info.show_close_button = false;
47 | this.info.close.connect(() => this.revealer.reveal_child = false);
48 |
49 | this.ok_button = this.info.add_button(_("OK"), 1);
50 | this.cancel_button = this.info.add_button(_("Cancel"), 0);
51 |
52 | this.revealer = new Gtk.Revealer();
53 | this.revealer.add(this.info);
54 | this.revealer.reveal_child = false;
55 | this.revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
56 |
57 | this.orientation = Gtk.Orientation.VERTICAL;
58 | this.add(this.revealer);
59 | }
60 |
61 | public class MessageOptions {
62 | public string prefill = "";
63 | public Gtk.MessageType type = Gtk.MessageType.QUESTION;
64 | public bool show_cancel = true; public bool show_entry = false;
65 | public string ok_text = _("OK"); public string cancel_text = _("Cancel");
66 | }
67 |
68 | public async bool message(string msg, MessageOptions opts) {
69 | body.label = msg;
70 | entry.text = opts.prefill;
71 | info.message_type = opts.type;
72 | cancel_button.visible = opts.show_cancel;
73 | entry.visible = opts.show_entry;
74 | entry.no_show_all = !opts.show_entry;
75 | ok_button.label = opts.ok_text;
76 | cancel_button.label = opts.cancel_text;
77 |
78 | revealer.reveal_child = true;
79 |
80 | var response = 1;
81 | var handler = info.response.connect((id) => {
82 | response = id;
83 | message.callback();
84 | });
85 | yield;
86 | info.disconnect(handler);
87 | info.close();
88 |
89 | return (bool) response;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/ext/README.md:
--------------------------------------------------------------------------------
1 | This code contains smaller Vala libraries not commonly available in package repositories.
2 |
3 | So far it contains:
4 |
5 | * https://github.com/bleakgrey/liberate
6 |
--------------------------------------------------------------------------------
/src/ext/liberate/Reader.vala:
--------------------------------------------------------------------------------
1 | using Gtk;
2 | using GLib;
3 | using WebKit;
4 |
5 | public class Liberate.Reader: Grid {
6 |
7 | protected WebKit.WebView view;
8 | protected WebKit.Settings settings;
9 | protected WebKit.UserContentManager content;
10 |
11 | public signal void progress (double fraction, bool ready);
12 | public string theme {get; set;}
13 | public string url {get; set;}
14 |
15 | construct {
16 | halign = Align.FILL;
17 | valign = Align.FILL;
18 |
19 | settings = new WebKit.Settings ();
20 | settings.enable_smooth_scrolling = true;
21 | settings.enable_javascript = false;
22 | settings.javascript_can_access_clipboard = false;
23 | settings.javascript_can_open_windows_automatically = false;
24 | settings.enable_java = false;
25 | settings.enable_media_stream = false;
26 | settings.enable_mediasource = false;
27 | settings.enable_plugins = false;
28 | settings.enable_html5_database = false;
29 | settings.enable_html5_local_storage = false;
30 | settings.enable_webaudio = false;
31 | settings.enable_webgl = false;
32 |
33 | content = new UserContentManager ();
34 | content.register_script_message_handler (HANDLER);
35 |
36 | view = new WebView.with_user_content_manager (content);
37 | view.expand = true;
38 | view.settings = settings;
39 | attach (view, 0, 0);
40 | view.visible = false;
41 |
42 | view.notify["estimated-load-progress"].connect (on_progress);
43 | notify["theme"].connect (() => {
44 | apply_theme (view, theme);
45 | });
46 |
47 | content.script_message_received.connect (result => {
48 | var msg = decode_message (result);
49 | if (msg != "")
50 | on_message (msg);
51 | else
52 | warning ("Caught JS error on receiving bridge message");
53 | });
54 | view.load_changed.connect (on_patch_request);
55 |
56 | notify["url"].connect (() => {
57 | debug ("Navigating to %s", url);
58 | view.load_uri (url);
59 | });
60 | }
61 |
62 | public Reader (string theme = "light") {
63 | this.theme = theme;
64 | }
65 |
66 | public Reader.with_url (string url, string theme = "light") {
67 | this.theme = theme;
68 | this.url = url;
69 | }
70 |
71 | protected bool on_message (string text) {
72 | debug ("Bridge message: %s", text);
73 |
74 | if (text == MSG_PATCHED) {
75 | progress (1, false);
76 | view.load_changed.disconnect (on_patch_request);
77 | }
78 | return true;
79 | }
80 |
81 | protected void on_progress () {
82 | progress (view.estimated_load_progress, view.is_loading);
83 | }
84 |
85 | protected void on_patch_request (LoadEvent ev) {
86 | if (ev != LoadEvent.FINISHED)
87 | return;
88 |
89 | debug ("Page load complete");
90 | settings.set_enable_javascript (true);
91 | Liberate.read (view, theme);
92 | view.visible = true;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/src/ext/liberate/data/Readability-readerable.js:
--------------------------------------------------------------------------------
1 | /* eslint-env es6:false */
2 | /* globals exports */
3 | /*
4 | * Copyright (c) 2010 Arc90 Inc
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | /*
20 | * This code is heavily based on Arc90's readability.js (1.7.1) script
21 | * available at: http://code.google.com/p/arc90labs-readability
22 | */
23 |
24 | var REGEXPS = {
25 | // NOTE: These two regular expressions are duplicated in
26 | // Readability.js. Please keep both copies in sync.
27 | unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|gdpr|header|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote/i,
28 | okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
29 | };
30 |
31 | function isNodeVisible(node) {
32 | // Have to null-check node.style to deal with SVG and MathML nodes.
33 | return (!node.style || node.style.display != "none") && !node.hasAttribute("hidden");
34 | }
35 |
36 | /**
37 | * Decides whether or not the document is reader-able without parsing the whole thing.
38 | *
39 | * @return boolean Whether or not we suspect Readability.parse() will suceeed at returning an article object.
40 | */
41 | function isProbablyReaderable(doc, isVisible) {
42 | if (!isVisible) {
43 | isVisible = isNodeVisible;
44 | }
45 |
46 | var nodes = doc.querySelectorAll("p, pre");
47 |
48 | // Get
nodes which have node(s) and append them into the `nodes` variable.
49 | // Some articles' DOM structures might look like
50 | //