#!/usr/bin/python3

# WebWindow v2.0 (c) Opinsys Oy 2018

import sys
import argparse


def check_positive(value):
    i = int(value)

    if i < 1:
        raise argparse.ArgumentTypeError(
            '"{0}" is not valid, must be an integer larger than zero'.
            format(value))

    return i


epilog = """
WebWindow opens the specified URL in a small embedded WebKit browser
window.

JavaScript is disabled and the right-click context menu is severely
restricted.  There are no tabs, history, cookies
or address bar.  No data is written on the disk.

Links can be clicked and they work normally, so you should not use
this to open pages that contain links to outside of the "sandbox".

The URL can be any valid HTTP/HTTPS URL, or a local HTML file that
is referenced using the file:// protocol.

If the URL is not valid, the window will remain empty!
"""

parser = argparse.ArgumentParser(
    formatter_class=argparse.RawDescriptionHelpFormatter,
    epilog=epilog)

parser.add_argument('--width',
                    required=False,
                    default=800,
                    help='window width in pixels (default is 800)',
                    type=check_positive)

parser.add_argument('--height',
                    required=False,
                    default=600,
                    help='window height in pixels (default is 600)',
                    type=check_positive)

parser.add_argument('--title',
                    required=False,
                    default='WebWindow',
                    help='window title')

parser.add_argument('--fixed',
                    action='store_true',
                    required=False,
                    default=False,
                    help='prevent the window from being resized')

parser.add_argument('--enable-js',
                    action='store_true',
                    required=False,
                    default=False,
                    help='enable JavaScript (be careful!)')

required = parser.add_argument_group('required arguments')

required.add_argument('--url',
                      required=True,
                      type=str,
                      help='the URL to open')

args = parser.parse_args()

import gi
gi.require_version('Gtk', '4.0')        # explicitly require Gtk3, not Gtk2
gi.require_version('WebKit', '6.0')     # explicitly require version 4
from gi.repository import Gtk, WebKit

class Browser(Gtk.Application):
    def __init__(self, params):
        super().__init__(application_id='puavo.webwindow')
        self.params = params


    def do_activate(self):
        self.win = Gtk.ApplicationWindow(application=self)

        self.win.set_title(self.params.title)
        self.win.set_size_request(self.params.width, self.params.height)

        if args.fixed:
            self.win.set_resizable(False)

        # Create an ephemeral context. Ephemeral contexts don't cache
        # anything on disk. Or so the documentation says...
        # (If this caches something somewhere, I can't find it.)
        session = WebKit.NetworkSession.new_ephemeral()
        self.webview = WebKit.WebView(network_session=session)

        settings = WebKit.Settings()

        if not args.enable_js:
            # Disable JavaScript
            settings.set_enable_javascript(False)

        self.webview.set_settings(settings)

        # Block the context menu
        self.webview.connect('context_menu', self.block_context_menu)

        # Listen for load events
        self.failed = False
        self.webview.connect('load-failed',  self.on_load_failed)
        self.webview.connect('load-changed', self.on_load_changed)

        self.webview.show()
        self.win.set_child(self.webview)

        self.win.show()

        self.webview.load_uri(args.url)


    def on_load_failed(self, webview, event, url, error):
        print('{"state":"failed"}', file=sys.stderr)
        self.failed = True


    def on_load_changed(self, webview, event):
        if event == WebKit.LoadEvent.FINISHED and not self.failed:
            print('{"state":"ok","title":"' + str(webview.get_title()) + '"}')


    def block_context_menu(self, webview, context_menu, event, hit_test_result):
        if hit_test_result.context_is_editable():
            # Permit cut/copy/paste on edit fields...
            return False

        # ...but block the menu everywhere else
        return True


webwindow = Browser(args)
webwindow.run()
