Added webkit ui class with browser to python calling logic
This commit is contained in:
		| @@ -6,6 +6,7 @@ gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
| from ..widgets.webkit_ui import WebkitUI | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -41,6 +42,7 @@ class CenterContainer(Gtk.Box): | ||||
|  | ||||
|         self.add(button) | ||||
|         self.add(glade_box) | ||||
|         self.add( WebkitUI() ) | ||||
|  | ||||
|     def _hello_world(self, widget = None, eve = None): | ||||
|         logger.debug("Hello, World!") | ||||
|         logger.debug("Hello, World!") | ||||
|   | ||||
							
								
								
									
										82
									
								
								src/core/widgets/webkit_ui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/core/widgets/webkit_ui.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
|  | ||||
|  | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gdk', '3.0') | ||||
| gi.require_version('WebKit2', '4.0') | ||||
| from gi.repository import Gdk | ||||
| from gi.repository import WebKit2 | ||||
|  | ||||
| # Application imports | ||||
|  | ||||
|  | ||||
|  | ||||
| class WebkitUI(WebKit2.WebView): | ||||
|     def __init__(self): | ||||
|         super(WebkitUI, self).__init__() | ||||
|  | ||||
|         self._setup_styling() | ||||
|         self._subscribe_to_events() | ||||
|         self._load_view() | ||||
|         self._setup_content_manager() | ||||
|          | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
|     def _setup_styling(self): | ||||
|         self.set_vexpand(True) | ||||
|         self.set_hexpand(True) | ||||
|         self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) ) | ||||
|  | ||||
|     def _subscribe_to_events(self): | ||||
|         # event_system.subscribe("handle_file_from_ipc", self.handle_file_from_ipc) | ||||
|         ... | ||||
|      | ||||
|     def _load_settings(self): | ||||
|         self.set_settings( WebkitUISettings() ) | ||||
|  | ||||
|     def _load_view(self): | ||||
|         path = settings_manager.get_context_path() | ||||
|         data = settings_manager.wrap_html_to_body("") | ||||
|         self.load_html(content = data, base_uri = f"file://{path}/") | ||||
|  | ||||
|     def _setup_content_manager(self): | ||||
|         content_manager = self.get_user_content_manager() | ||||
|         content_manager.register_script_message_handler("backend") | ||||
|         content_manager.connect("script-message-received", self._process_js_message) | ||||
|  | ||||
|     def _process_js_message(self, user_content_manager, js_result): | ||||
|         js_value = js_result.get_js_value() | ||||
|         print(js_value.to_string()) | ||||
|         # self._web_view.run_javascript("do_stuff()", None, None) | ||||
|  | ||||
|  | ||||
| class WebkitUISettings(WebKit2.Settings): | ||||
|     def __init__(self): | ||||
|         super(WebkitUISettings, self).__init__() | ||||
|          | ||||
|         self._set_default_settings() | ||||
|  | ||||
|  | ||||
|     # Note: Highly insecure setup but most "app" like setup I could think of. | ||||
|     #       Audit heavily any scripts/links ran/clicked under this setup!  | ||||
|     def _set_default_settings(self): | ||||
|         self.set_enable_offline_web_application_cache(False) | ||||
|         self.enable_html5_local_storage(False) | ||||
|         self.enable_html5_database(False) | ||||
|         self.enable_xss_auditor(False) | ||||
|         self.set_enable_hyperlink_auditing(False) | ||||
|         self.set_enable_tabs_to_links(False) | ||||
|         self.enable_fullscreen(False) | ||||
|         self.set_print_backgrounds(False) | ||||
|         self.enable_webaudio(False) | ||||
|         self.set_enable_page_cache(False) | ||||
|  | ||||
|         self.enable_accelerated_2d_canvas(True) | ||||
|         self.set_allow_file_access_from_file_urls(True) | ||||
|         self.set_allow_universal_access_from_file_urls(True) | ||||
|         self.set_enable_webrtc(True) | ||||
|  | ||||
|         sdelf.set_user_agent(f"{app_name}") | ||||
| @@ -13,6 +13,7 @@ from os import mkdir | ||||
| # Application imports | ||||
| from ..singleton import Singleton | ||||
| from .start_check_mixin import StartCheckMixin | ||||
| from .markdown_template_mixin import MarkdownTemplateMixin | ||||
| from .options.settings import Settings | ||||
|  | ||||
|  | ||||
| @@ -22,7 +23,7 @@ class MissingConfigError(Exception): | ||||
|  | ||||
|  | ||||
|  | ||||
| class SettingsManager(StartCheckMixin, Singleton): | ||||
| class SettingsManager(StartCheckMixin, MarkdownTemplateMixin, Singleton): | ||||
|     def __init__(self): | ||||
|         self._SCRIPT_PTH        = path.dirname(path.realpath(__file__)) | ||||
|         self._USER_HOME         = path.expanduser('~') | ||||
| @@ -30,6 +31,7 @@ class SettingsManager(StartCheckMixin, Singleton): | ||||
|         self._USR_PATH          = f"/usr/share/{app_name.lower()}" | ||||
|         self._USR_CONFIG_FILE   = f"{self._USR_PATH}/settings.json" | ||||
|  | ||||
|         self._CONTEXT_PATH      = f"{self._HOME_CONFIG_PATH}/context_path" | ||||
|         self._PLUGINS_PATH      = f"{self._HOME_CONFIG_PATH}/plugins" | ||||
|         self._DEFAULT_ICONS     = f"{self._HOME_CONFIG_PATH}/icons" | ||||
|         self._CONFIG_FILE       = f"{self._HOME_CONFIG_PATH}/settings.json" | ||||
| @@ -146,6 +148,7 @@ class SettingsManager(StartCheckMixin, Singleton): | ||||
|     def get_ui_widgets_path(self)    -> str: return self._UI_WIDEGTS_PATH | ||||
|     def get_context_menu_data(self)  -> str: return self._context_menu_data | ||||
|  | ||||
|     def get_context_path(self)     -> str:   return self._CONTEXT_PATH | ||||
|     def get_plugins_path(self)     -> str:   return self._PLUGINS_PATH | ||||
|     def get_icon_theme(self)       -> str:   return self._ICON_THEME | ||||
|     def get_css_file(self)         -> str:   return self._CSS_FILE | ||||
|   | ||||
							
								
								
									
										60
									
								
								src/utils/settings_manager/markdown_template_mixin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/utils/settings_manager/markdown_template_mixin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Lib imports | ||||
|  | ||||
| # Application imports | ||||
|  | ||||
|  | ||||
|  | ||||
| class MarkdownTemplateMixin: | ||||
|     def wrap_html_to_body(self, html): | ||||
|         return f"""\ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en" dir="ltr"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>Markdown View</title> | ||||
|     <style media="screen"> | ||||
|         html, body {{ | ||||
|             display: block; | ||||
|             background-color: #32383e00; | ||||
|             color: #ffffff; | ||||
|             text-wrap: wrap; | ||||
|         }} | ||||
|          | ||||
|         img {{ | ||||
|             width: 100%; | ||||
|             height: auto; | ||||
|         }} | ||||
|          | ||||
|         code {{ | ||||
|             border: 1px solid #32383e; | ||||
|             background-color: #32383e; | ||||
|             padding: 4px; | ||||
|         }} | ||||
|     </style> | ||||
|  | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
|     {html} | ||||
|     <span>Button in WebView | ||||
|         <button onclick="say_hello()">Button in WebView</button> | ||||
|     </span> | ||||
|  | ||||
|  | ||||
|     <!-- For internal scripts... --> | ||||
|     <script src="js/libs/jquery-3.7.1.min.js"></script> | ||||
|  | ||||
|     <!-- For Bootstrap... --> | ||||
|     <script src="resources/js/libs/bootstrap5/bootstrap.bundle.min.js"></script> | ||||
|  | ||||
|     <!-- For Application... --> | ||||
|     <script src="resources/js/ui-logic.js"></script> | ||||
|     <script src="resources/js/post-ajax.js"></script> | ||||
|     <script src="resources/js/ajax.js"></script> | ||||
|     <script src="resources/js/events.js"></script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| """ | ||||
| @@ -0,0 +1,43 @@ | ||||
| const logUIExceptionAjax = (data, action = "client-exception-logger") => { | ||||
|     const postArgs = 'exception_data=' + data; | ||||
|  | ||||
|     messenger.backend.postMessage(postArgs); | ||||
| } | ||||
|  | ||||
|  | ||||
| const doAjax = (actionPath, data, action) => { | ||||
|     let xhttp = new XMLHttpRequest(); | ||||
|  | ||||
|     xhttp.onreadystatechange = function() { | ||||
|         if (this.readyState === 4 && this.status === 200) { | ||||
|             if (this.responseText != null) {  // this.responseXML if getting XML data | ||||
|                 postAjaxController(JSON.parse(this.responseText), action); | ||||
|             } else { | ||||
|                 let type = "danger" | ||||
|                 let msg  = "No content returned. Check the target path."; | ||||
|                 data     = '{"message": { "type": "' + type +  '", "text": "' + text + '" } }' | ||||
|                 postAjaxController(JSON.parse(data)); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     xhttp.open("POST", actionPath, true); | ||||
|     xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | ||||
|     // Force return to be JSON NOTE: Use application/xml to force XML | ||||
|     xhttp.overrideMimeType('application/json'); | ||||
|     xhttp.send(data); | ||||
| } | ||||
|   | ||||
| const formatURL = (basePath) => { | ||||
|     url = window.location.href; | ||||
|     if ( url.endsWith('/') ) | ||||
|         return url + basePath; | ||||
|     else | ||||
|         return url + '/' + basePath; | ||||
| } | ||||
|  | ||||
| const fetchData = async (url) => { | ||||
|     let response = null; | ||||
|     response = await fetch(url); | ||||
|     return await response.json(); | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| const menu = document.querySelector(".menu"); | ||||
| let menuVisible = false; | ||||
|  | ||||
| const toggleMenu = command => { | ||||
|     menu.style.display = command === "show" ? "block" : "none"; | ||||
|     menu.style.zIndex  = "9999"; | ||||
|     menuVisible        = !menuVisible; | ||||
| }; | ||||
|  | ||||
| const setPosition = ({ top, left }) => { | ||||
|     menu.style.left = `${left}px`; | ||||
|     menu.style.top  = `${top}px`; | ||||
|     toggleMenu("show"); | ||||
| }; | ||||
|  | ||||
| window.addEventListener("click", e => { | ||||
|     if(menuVisible) toggleMenu("hide"); | ||||
| }); | ||||
|  | ||||
| window.addEventListener("contextmenu", e => { | ||||
|     e.preventDefault(); | ||||
|     const origin = { | ||||
|         left: e.pageX, | ||||
|         top: e.pageY | ||||
|     }; | ||||
|     setPosition(origin); | ||||
|     return false; | ||||
| }); | ||||
| @@ -0,0 +1,22 @@ | ||||
| const messenger = window.webkit.messageHandlers | ||||
|  | ||||
| window.onload = (eve) => { | ||||
|     console.log("Loaded..."); | ||||
| } | ||||
|  | ||||
| window.onerror = function(msg, url, line, col, error) { | ||||
|    // Note that col & error are new to the HTML 5 spec and may not be supported in every browser. | ||||
|    const suppressErrorAlert = false; | ||||
|    let extra = !col ? '' : '\ncolumn: ' + col; | ||||
|    extra += !error ? '' : '\nerror: ' + error; | ||||
|    const data = `Error:  ${msg} \nurl: ${url} \nline: ${line}  ${extra}` | ||||
|  | ||||
|    logUIExceptionAjax(data); | ||||
|  | ||||
|    // If you return true, then error alerts (like in older versions of Internet Explorer) will be suppressed. | ||||
|    return suppressErrorAlert; | ||||
| }; | ||||
|  | ||||
| const say_hello = () => { | ||||
|     messenger.backend.postMessage("Hello, World!"); | ||||
| } | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								user_config/usr/share/app_name/context_path/resources/js/libs/jquery-3.7.1.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								user_config/usr/share/app_name/context_path/resources/js/libs/jquery-3.7.1.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -0,0 +1,11 @@ | ||||
| const postAjaxController = (data, action) => { | ||||
|     if (data.message) { | ||||
|         message = data.message | ||||
|  | ||||
|         if (action == "someAction" && message.type.includes("success")) { | ||||
|             console.log("Success!"); | ||||
|         } | ||||
|  | ||||
|         displayMessage(message.text, message.type, 0); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| // Message handler | ||||
| const displayMessage = (message, type, timeout, msgWindow = "page-alert-zone") => { | ||||
|     let alertField  = document.getElementById(msgWindow); | ||||
|     let divElm      = document.createElement("DIV"); | ||||
|     let btnElm      = document.createElement("BUTTON"); | ||||
|     let spnElm      = document.createElement("SPAN"); | ||||
|     let textElm     = document.createTextNode(message); | ||||
|  | ||||
|     divElm.setAttribute("class", "alert alert-dismissible fade show alert-" + type); | ||||
|     divElm.setAttribute("role", "alert"); | ||||
|     divElm.appendChild(textElm); | ||||
|     btnElm.type     = "button"; | ||||
|     textElm         = document.createTextNode("X"); | ||||
|     btnElm.setAttribute("class", "btn-dark btn-close"); | ||||
|     btnElm.setAttribute("data-bs-dismiss", "alert"); | ||||
|     btnElm.setAttribute("aria-label", "close"); | ||||
|     spnElm.setAttribute("aria-hidden", "true"); | ||||
|     spnElm.appendChild(textElm); | ||||
|     btnElm.appendChild(spnElm); | ||||
|     divElm.appendChild(btnElm); | ||||
|     alertField.appendChild(divElm); | ||||
|  | ||||
|     if (timeout > 0) { | ||||
|         setTimeout(function () { | ||||
|             clearChildNodes(alertField); | ||||
|         }, timeout * 1000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| const clearChildNodes = (parent) => { | ||||
|     while (parent.firstChild) { | ||||
|         parent.removeChild(parent.firstChild); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| // Cache Buster | ||||
| const clearCache = () => { | ||||
|     const rep = /.*\?.*/; | ||||
|     let links     = document.getElementsByTagName('link'), | ||||
|         scripts   = document.getElementsByTagName('script'), | ||||
|         video     = document.getElementsByTagName('video'), | ||||
|         process_scripts = false; | ||||
|  | ||||
|     for (let i = 0; i < links.length; i++) { | ||||
|         let link = links[i], | ||||
|         href = link.href; | ||||
|         if(rep.test(href)) { | ||||
|             link.href = href + '&' + Date.now(); | ||||
|         } else { | ||||
|             link.href = href + '?' + Date.now(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     if(process_scripts) { | ||||
|         for (let i = 0; i < scripts.length; i++) { | ||||
|             let script = scripts[i], | ||||
|             src = script.src; | ||||
|             if(rep.test(src)) { | ||||
|                 script.src = src+'&'+Date.now(); | ||||
|             } else { | ||||
|                 script.src = src+'?'+Date.now(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user