Last active
October 29, 2022 14:26
-
-
Save nikolay-n/359474898f61c3479e0be79e3b9957d3 to your computer and use it in GitHub Desktop.
Revisions
-
nikolay-n revised this gist
Dec 6, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -287,7 +287,7 @@ class DataQueue{ this.queue = []; this.cb = cb; this.state = false; this.interval = setInterval(this._queueLoop.bind(this), 0); } _queueLoop(){ -
nikolay-n revised this gist
Dec 6, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -685,12 +685,12 @@ class PrefDataView { <div id="prefview"></div> <script id="tpl-proc" type="text/x-template"> <div class="proc-container"> <div class="proc-title" title="{%PROC_PATH%}"><a href="javascript:void(0)" class="expand">-</a> {%PROC%}</div> </div> </script> <script id="tpl-domain" type="text/x-template"> <div class="domain-container pcont"> <div class="domain-title"><a href="javascript:void(0)" class="expand">-</a> {%DOMAIN%}</div> <div class="pref-container pcont"></div> </div> </script> -
nikolay-n created this gist
Dec 6, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,929 @@ #!/usr/bin/python2.7 # -*- coding: utf-8 -*- ''' Defaults Monitor - tool to sniff defaults keys and values using unified log to launch use standard python 2.7, eg python2.7 ./defsmon.py ''' import os import argparse import sys import tempfile as tmp import re import base64 import subprocess import AppKit import WebKit import ctypes import ctypes.util import threading import json from PyObjCTools import AppHelper from objc import _objc, nil, super, pyobjc_unicode, registerMetaDataForSelector import Foundation from Foundation import ( YES, NSBundle, NSLocale, NSLocalizedStringFromTableInBundle, NSAutoreleasePool, NSURL) ################################################ # DEFINE TYPES, CONST, FUNCS AND LOAD LIBRARIES ################################################ # ApplicationServices ################################################ class ProcessSerialNumber(ctypes.Structure): _fields_ = [ ('highLongOfPSN', ctypes.c_uint32), ('lowLongOfPSN', ctypes.c_uint32), ] kNoProcess = 0 kSystemProcess = 1 kCurrentProcess = 2 kProcessTransformToForegroundApplication = 1 kProcessTransformToBackgroundApplication = 2 kProcessTransformToUIElementAppication = 4 appSrvc = ctypes.CDLL('/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices') appSrvc.TransformProcessType.argtypes = [ctypes.POINTER(ProcessSerialNumber), ctypes.c_uint32] appSrvc.SetFrontProcess.argtypes = [ctypes.POINTER(ProcessSerialNumber)] psn = ProcessSerialNumber(0, kCurrentProcess) appSrvc.TransformProcessType(psn, kProcessTransformToForegroundApplication) appSrvc.SetFrontProcess(psn) ################################################ # WebKit stuff ################################################ _eval_js_metadata = { 'arguments': { 3: { 'callable': { 'retval': { 'type': b'v' }, 'arguments': { 0: { 'type': b'^v' }, 1: { 'type': b'@' }, 2: { 'type': b'@' }}}}}} ################################################ # Other constants ################################################ APP_NAME = "Defaults Monitor" PROFILES_BIN = "/usr/bin/profiles" # Following profile enables private data logging that allows us to get defaults values, without it there will be "<private>" values # more info: https://georgegarside.com/blog/macos/sierra-console-private/ PROFILE_ID = "com.georgegarside.profile.logprivate" PROFILE_FILE_NAME = "enable-unified-log-private-data.mobileconfig" # Profile signed and contains binary characters -> base64 PROFILE_DATA = base64.b64decode("MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCAJIAEggUKPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+UGF5bG9hZENvbnRlbnQ8L2tleT4KCTxhcnJheT4KCQk8ZGljdD4KCQkJPGtleT5QYXlsb2FkRGlzcGxheU5hbWU8L2tleT4KCQkJPHN0cmluZz5NYW5hZ2VkQ2xpZW50IGxvZ2dpbmc8L3N0cmluZz4KCQkJPGtleT5QYXlsb2FkRW5hYmxlZDwva2V5PgoJCQk8dHJ1ZS8+CgkJCTxrZXk+UGF5bG9hZElkZW50aWZpZXI8L2tleT4KCQkJPHN0cmluZz5jb20uYXBwbGUubG9nZ2luZy5NYW5hZ2VkQ2xpZW50LjE8L3N0cmluZz4KCQkJPGtleT5QYXlsb2FkVHlwZTwva2V5PgoJCQk8c3RyaW5nPmNvbS5hcHBsZS5zeXN0ZW0ubG9nZ2luZzwvc3RyaW5nPgoJCQk8a2V5PlBheWxvYWRVVUlEPC9rZXk+CgkJCTxzdHJpbmc+RUQ1REUzMDctQTVGQy00MzRGLUFEODgtMTg3Njc3RjAyMjIyPC9zdHJpbmc+CgkJCTxrZXk+UGF5bG9hZFZlcnNpb248L2tleT4KCQkJPGludGVnZXI+MTwvaW50ZWdlcj4KCQkJPGtleT5TeXN0ZW08L2tleT4KCQkJPGRpY3Q+CgkJCQk8a2V5PkVuYWJsZS1Qcml2YXRlLURhdGE8L2tleT4KCQkJCTx0cnVlLz4KCQkJPC9kaWN0PgoJCTwvZGljdD4KCTwvYXJyYXk+Cgk8a2V5PlBheWxvYWREZXNjcmlwdGlvbjwva2V5PgoJPHN0cmluZz5TaG93ICZsdDtwcml2YXRlJmd0OyBsb2dzPC9zdHJpbmc+Cgk8a2V5PlBheWxvYWREaXNwbGF5TmFtZTwva2V5PgoJPHN0cmlu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") LOG_BIN = "/usr/bin/log" # The UI is web based because ¯\_(ツ)_/¯ HTML_UI = '''<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Prefs Monitor</title> <style type="text/css"> * { font-family: "PT Sans"; font-size: 12pt; } html, body { padding:0px; margin:0px; } body { width:100%; height:100%; overflow-y: auto; background: #323232; color:#fff; } #navbar { position: fixed; top:0px; left: 0px; width: 100%; height: 40px; background: #2d2d2d; border-bottom: #111 solid 1px; white-space: nowrap; overflow: hidden; -webkit-user-select: none; user-select: none; cursor: default; } #navbar ul { padding: 0px; margin: 0px; list-style-type: none; } #navbar ul li{ padding: 0px 5px 0px; margin: 0px; display: block; float: left; line-height: 40px; } #navbar ul li input{ width:90px; transition: width .2s ease-in-out; } #navbar ul li input.wide{ width:180px; } #navbar ul li.title{ font-weight: bold; padding: 0px 15px 0px; color: orange; } #navbar ul li.sep{ width: 15px; } #navbar ul li.clear{ float: right; padding-right: 10px; } #clear{ text-decoration: none; cursor: pointer; display: inline-block; width:20px; height:20px; background: #ffffff01; } #prefview { padding-top:50px; padding-left: 10px; } .proc-title{ color: #1bc0e5; font-weight: bold; } .domain-title { color: #e3c700; font-weight: bold; } .pcont { padding-left: 20px; } .has-value { color: #56d523; } .no-value { color: #d05428; } a.expand{ text-decoration: none; color: #fff; display: inline-block; width:8px; } .pref-key { display: inline-block; background: transparent; /*min-width: 30%;*/ } .fade{ animation: fade 1s normal forwards; } @keyframes fade { from {background-color: blue;} to {background-color: #ffffff00;} } .pref-value{ color: #999; } .collapse .domain-container{ display: none; } .domain-container.collapse .pref-container{ display: none; } .proc-container, .domain-container{ padding-bottom: 5px; padding-top: 5px; border-bottom: #3a3a3a solid 1px; } .proc-container .domain-container{ border-bottom: none; } .domain-container { padding-left: 0px; padding-bottom: 10px; } .proc-container .domain-container{ padding-left: 20px; padding-bottom: 5px; padding-top: 5px; } .pref-container{ padding-top: 5px; } .hidden{ display: none; } </style> <script> window.addEventListener('DOMContentLoaded', (event) => main()); function main(){ var filter = new Filter(); var navBar = new NavBar(document.querySelector("#navbar"), filter); var prefDataView = new PrefDataView(document.querySelector("#prefview"), filter); navBar.render(); var dq = window.DQ = new DataQueue(function(data){ prefDataView.render(data); }); dq.state = true; setTimeout(()=> postMsg("loaded"), 100); } function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function postMsg(message){ window.webkit.messageHandlers.browserDelegate.postMessage(message); } class DataQueue{ constructor(cb){ this.queue = []; this.cb = cb; this.state = false; this.interval = setInterval(this._queueLoop.bind(this), 30); } _queueLoop(){ if(!this.state) return; if(this.queue.length){ this.cb(this.queue.shift()) } } get state(){ return this._state; } set state(val){ this._state = val; } enqueue(data){ this.queue.push(data) } } class Filter { constructor() { this.cbs = [] } get group(){ return this._group } set group(val){ this._group = val; this._onChange("group"); } set clear(val){ this._onChange("clear"); } get key(){ return this._key } set key(val){ this._key = val; this._onChange("key"); } get process(){ return this._process; } set process(val){ this._process = val; this._onChange("process"); } get domain(){ return this._domain; } set domain(val){ this._domain = val; this._onChange("domain"); } get values(){ return this._values; } set values(val){ this._values = val; this._onChange("values"); } _onChange(field){ this.cbs.forEach( cb => setTimeout(() => cb(field),0)); } registerCallback(cb){ this.cbs.push(cb) } unregisterCallback(cb){ this.cbs = this.cbs.filter( c => c != cb) } } class NavBar { constructor(el, filter) { this.el = el; this.filter = filter; this.groupSelectEl = el.querySelector("#group") this.procFilterEl = el.querySelector("#procname") this.domainFilterEl = el.querySelector("#domain") this.keyFilterEl = el.querySelector("#key") this.valueFilterEl = el.querySelector("#valvis") this.clearEl = el.querySelector("#clear") } render(data){ self = this this.groupSelectEl.addEventListener("change", function () { self.filter.group = this.value; if(this.value == "process"){ self.procFilterEl.classList.remove("hidden"); }else{ self.procFilterEl.classList.add("hidden"); } }); this.groupSelectEl.dispatchEvent(new Event("change")); this.procFilterEl.addEventListener("input", function () { self.filter.process = this.value; }); this.domainFilterEl.addEventListener("input", function () { self.filter.domain = this.value; }); this.keyFilterEl.addEventListener("input", function () { self.filter.key = this.value; }); this.valueFilterEl.addEventListener("change", function () { self.filter.values = this.value; }); this.clearEl.addEventListener("click", function () { self.filter.clear = true; }); [this.procFilterEl, this.domainFilterEl, this.keyFilterEl].forEach(input => { input.setAttribute('autocomplete', 'off') input.setAttribute('autocorrect', 'off') input.setAttribute('autocapitalize', 'off') input.setAttribute('spellcheck', false) input.addEventListener("focus", () => input.classList.add("wide")); input.addEventListener("blur", () => input.classList.remove("wide")); }) } } class PrefDataView { constructor(el, filter) { this.el = el; this.procGroupEl = document.createElement("div"); this.el.appendChild(this.procGroupEl); this.domGroupEl = document.createElement("div"); this.domGroupEl.classList.add("hidden"); this.el.appendChild(this.domGroupEl); this.filter = filter; this.filter.registerCallback(this.render.bind(this)); this.cache = []; this.tplCache = {}; this.procs = {}; this.domains = {}; } renderTpl(tpl, data){ var tplContent = this.tplCache[tpl]; if(!tplContent){ tplContent = document.querySelector("#"+tpl).innerHTML; this.tplCache[tpl] = tplContent; } for (var k in data){ if (data.hasOwnProperty(k)) { tplContent = tplContent.replace(new RegExp(`{%${k}%}`,"gi"), data[k]); } } var elem = document.createElement("div"); elem.innerHTML = tplContent.trim(); return elem.firstChild; } setupExpand(el, expand){ var expEl = el.querySelector(".expand"); expEl.addEventListener("click", function(){ var clsLs = expEl.parentNode.parentNode.classList; var sym = clsLs.contains("collapse")? "-": "+"; clsLs.toggle("collapse"); expEl.innerHTML = sym; }); } _insertBeforePref(data, prefs, el, parentEl){ var keys = Object.keys(prefs); keys.push(data.key); keys.sort(); var idx = keys.indexOf(data.key); if(keys.length == 1 || (idx + 1) == keys.length){ parentEl.appendChild(el); }else{ var afterEl = prefs[keys[idx+1]]["el"]; parentEl.insertBefore(el, afterEl); } } render(data) { var self = this; var dataType= typeof(data); if(dataType == "object") { var proc = this.procs[data.process]; if (!proc){ var procEl = this.renderTpl("tpl-proc", {"proc": data.process, "proc_path": data.processPath}); this.procGroupEl.appendChild(procEl); this.setupExpand(procEl); proc = { "el": procEl, "expand": true, "domains": {} }; this.procs[data.process] = proc; } var domains = [[proc["el"], proc["domains"]], [this.domGroupEl, this.domains]]; domains.forEach(function (el) { var parentEl = el[0]; var domains = el[1]; var domain = domains[data.domain]; if (!domain){ var domainEl = self.renderTpl("tpl-domain", {"domain": data.domain}); var prefsContEl = domainEl.querySelector(".pref-container") parentEl.appendChild(domainEl); self.setupExpand(domainEl); var domain_data = { "el": domainEl, "prefsEl": prefsContEl, "expand": true, "prefs": {} }; domains[data.domain] = domain_data; } }); [proc["domains"][data.domain], this.domains[data.domain]].forEach(function (domain) { var pref = domain["prefs"][data.key]; if (!pref){ var prefEl = self.renderTpl("tpl-kv", { "key": data.key, "value": data.value == null ? "<no value>": escapeHtml(data.value), "key_cls": data.value == null ? "no-value": "has-value" }); pref = { "el": prefEl, "value": data.value }; self._insertBeforePref(data, domain["prefs"], prefEl, domain["prefsEl"]) domain["prefs"][data.key] = pref; prefEl.querySelector(".pref-key").addEventListener('animationend', function() { this.classList.toggle("fade"); }); } }) self.render(""); } else if (dataType == "string"){ if (data == "clear"){ this.procs = {}; this.domains = {}; this.procGroupEl.innerHTML = ""; this.domGroupEl.innerHTML = ""; // filter event occurred }else if(data == "group"){ var el = [this.procGroupEl, this.domGroupEl]; if (this.filter.group == "domain"){ el = el.reverse(); } el[0].classList.remove("hidden"); el[1].classList.add("hidden"); } else { var procs = Object.keys(this.procs); const domProc= '_domain_proc_'; procs.push(domProc); for (var p of procs.values()){ var isDomainProc = p == domProc? true:false; if (this.procs.hasOwnProperty(p) || isDomainProc) { var domains = isDomainProc ? this.domains: this.procs[p]["domains"]; if(!isDomainProc){ var rx = new RegExp("_no_match_"); try { rx = new RegExp(this.filter.process,"i"); } catch (error) { //console.error(error); } if(rx.test(p)){ this.procs[p]["el"].classList.remove("hidden"); }else{ this.procs[p]["el"].classList.add("hidden"); continue; } } var visibleDomains = 0; for (var d in domains){ if (domains.hasOwnProperty(d)) { var rx = new RegExp("_no_match_"); try { rx = new RegExp(this.filter.domain,"i"); } catch (error) { //console.error(error); } if(rx.test(d)){ domains[d]["el"].classList.remove("hidden"); visibleDomains++; }else{ domains[d]["el"].classList.add("hidden"); continue; } var prefs = domains[d]["prefs"]; var visiblePrefs = 0; for (var pref in prefs){ if (prefs.hasOwnProperty(pref)) { var rx = new RegExp("_no_match_"); try { rx = new RegExp(this.filter.key,"i"); } catch (error) { //console.error(error); } var hideByValue = false; if (this.filter.values == "value") hideByValue = prefs[pref]["value"] == null? true:false; else if (this.filter.values == "null") hideByValue = prefs[pref]["value"] == null? false:true; if(rx.test(pref) && !hideByValue){ prefs[pref]["el"].classList.remove("hidden"); visiblePrefs++; }else{ prefs[pref]["el"].classList.add("hidden"); continue; } } } if(!visiblePrefs){ domains[d]["el"].classList.add("hidden"); visibleDomains--; } } } if(!isDomainProc){ if(visibleDomains){ this.procs[p]["el"].classList.remove("hidden"); }else{ this.procs[p]["el"].classList.add("hidden"); } } } } } } } } </script> </head> <body> <div id="navbar"> <ul> <li class="title">⚙ Defaults Monitor</li> <li class="sep"></li> <li>group by:</li> <li> <select id="group"> <option value="process">process</option> <option value="domain">domain</option> </select> </li> <li>filter by:</li> <li><input placeholder="Process: Finder" type="search" id="procname"></li> <li><input placeholder="Domain: com.apple.Finder" type="search" id="domain"></li> <li><input placeholder="Key: ShowSidebar" type="search" id="key"></li> <li>values:</li> <li> <select id="valvis"> <option value="all">All</option> <option value="value">Has value</option> <option value="null">No value</option> </select> </li> <li class="clear"><a id="clear" title="Clear All" href="javascript:void(0)">🗑</a></li> </ul> </div> <div id="prefview"></div> <script id="tpl-proc" type="text/x-template"> <div class="proc-container"> <div class="proc-title" title="{%PROC_PATH%}"><a href="#" class="expand">-</a> {%PROC%}</div> </div> </script> <script id="tpl-domain" type="text/x-template"> <div class="domain-container pcont"> <div class="domain-title"><a href="#" class="expand">-</a> {%DOMAIN%}</div> <div class="pref-container pcont"></div> </div> </script> <script id="tpl-kv" type="text/x-template"> <div class="pref-kv"><span class="pref-key fade {%KEY_CLS%}">{%KEY%}</span> = <span class="pref-value">{%VALUE%}</span></div> </script> </body> </html>''' ################################################ # Main app stuff ################################################ class LogStreamReader: def __init__(self, cb): self.cb = cb def _thread(self): args = [LOG_BIN, "stream", "--style", "json","--predicate", 'category=="User Defaults"', "--info", "--debug"] f = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) event_json = "" while True: line = f.stdout.readline() if line.startswith("[{"): event_json = "{\n"; elif line.startswith("},{"): event_json += "}" self._process_event(event_json) event_json = "{\n"; else: event_json += line def _process_event(self, event_json): try: event_raw = json.loads(event_json) event = dict() matches = re.findall(r"looked up value (.+?) for key (.+?) in .*\(Domain: (.*?),", event_raw["eventMessage"], flags=re.DOTALL) if matches: event["value"] = matches[0][0] event["key"] = matches[0][1] event["domain"] = matches[0][2] else: matches = re.findall(r"found no value for key (.+?) in .*\(Domain: (.*?),", event_raw["eventMessage"], flags=re.DOTALL) if matches: event["value"] = None event["key"] = matches[0][0] event["domain"] = matches[0][1] if "domain" not in event: return event["process"] = os.path.basename(event_raw["processImagePath"]) event["processPath"] = event_raw["processImagePath"] event["library"] = os.path.basename(event_raw["senderImagePath"]) event["libraryPath"] = event_raw["senderImagePath"] self.cb(event) except Exception as e: pass def start(self): self.thread = threading.Thread(target=self._thread) self.thread.start() # self._thread() class DefaultsMonitorApp(AppKit.NSApplication): sharedApp = None bundle = None def finishLaunching(self): super(DefaultsMonitorApp, self).finishLaunching() self._window = None self._webkit = None self._logStream = LogStreamReader(self._onLogEvent) self._checkProfileInstalled( lambda: self._createWebkitWindow()) def sendEvent_(self, event): super(DefaultsMonitorApp, self).sendEvent_(event) def _checkProfileInstalled(self, cb): output = subprocess.check_output([PROFILES_BIN, "show", PROFILE_ID]).decode() if "There are no configuration profiles installed" in output: if self._confirm("To display configuration values need to enable PRIVATE DATA, this can be achieved by installing configuration profile. \n\nSystem Preferences app will be launched. After installation close System Preferences to continue. \n\nDo you want to install profile?"): def _install(): tmp_path = tmp.mktemp(suffix=".mobileconfig") with open(tmp_path, "w+b") as t: t.write(PROFILE_DATA) print("!!! Close System Preferences to continue") subprocess.check_call(["open","-W", tmp_path]) os.unlink(tmp_path) cb() AppHelper.callAfter(_install); return cb() def _alert(self, message): AppKit.NSRunningApplication.currentApplication().activateWithOptions_(AppKit.NSApplicationActivateIgnoringOtherApps) alert = AppKit.NSAlert.alloc().init() alert.setInformativeText_(message) alert.runModal() def _confirm(self, message, modal=True): AppKit.NSApplication.sharedApplication() AppKit.NSRunningApplication.currentApplication().activateWithOptions_(AppKit.NSApplicationActivateIgnoringOtherApps) alert = AppKit.NSAlert.alloc().init() alert.addButtonWithTitle_("Yes") alert.addButtonWithTitle_("No") alert.setMessageText_(message) alert.setAlertStyle_(AppKit.NSWarningAlertStyle) if alert.runModal() == AppKit.NSAlertFirstButtonReturn: return True else: return False def _createWebkitWindow(self): app = self class MainWindow(AppKit.NSWindow): def canBecomeKeyWindow(self): return True def canBecomeMainWindow(self): return True class WindowDelegate(AppKit.NSObject): #def windowShouldClose_(self, window): # return Foundation.YES def windowWillClose_(self, notification): app._terminate() rect = AppKit.NSMakeRect(0.0, 0.0, 1010, 500) window_mask = AppKit.NSTitledWindowMask | AppKit.NSClosableWindowMask | AppKit.NSMiniaturizableWindowMask | AppKit.NSResizableWindowMask window = MainWindow.alloc().\ initWithContentRect_styleMask_backing_defer_(rect, window_mask, AppKit.NSBackingStoreBuffered, False).retain() window.setTitle_(APP_NAME) window.setMinSize_(AppKit.NSSize(1010, 200)) # window.setAnimationBehavior_(AppKit.NSWindowAnimationBehaviorNone) frame = window.frame() frame.size.width = rect.size.width frame.size.height = rect.size.height class WebKitHost(WebKit.WKWebView): def performKeyEquivalent_(self, theEvent): return True webkit = WebKitHost.alloc().initWithFrame_(rect).retain() # webkit.setCustomUserAgent_(user_agent) window.setOpaque_(True) window.setHasShadow_(True) # window.setBackgroundColor_(AppKit.NSColor.colorWithSRGBRed_green_blue_alpha_(100, 100, 100, 0)) # webkit.setValue_forKey_(True, 'drawsTransparentBackground') window.setAlphaValue_(0.0) # window.setLevel_(AppKit.NSStatusWindowLevel) try: webkit.evaluateJavaScript_completionHandler_('', lambda a, b: None) except TypeError: registerMetaDataForSelector(b'WKWebView', b'evaluateJavaScript:completionHandler:', _eval_js_metadata) config = webkit.configuration() try: config.preferences().setValue_forKey_(Foundation.NO, 'backspaceKeyNavigationEnabled') config.preferences().setValue_forKey_(Foundation.YES, 'developerExtrasEnabled') except Exception as e: pass url = NSURL.URLWithString_("") webkit.loadHTMLString_baseURL_(HTML_UI, url) window.setContentView_(webkit) class BrowserDelegate(AppKit.NSObject): def userContentController_didReceiveScriptMessage_(self, controller, message): action = message.body() if action == "loaded": window.makeFirstResponder_(webkit) window.makeKeyAndOrderFront_(window) app.currentApp.activateWithOptions_(AppKit.NSApplicationActivateIgnoringOtherApps) window.setAlphaValue_(1.0) window.center() app._logStream.start() browserDelegate = BrowserDelegate.alloc().init().retain() config.userContentController().addScriptMessageHandler_name_(browserDelegate, 'browserDelegate') windowDelegate = WindowDelegate.alloc().init().retain() window.setDelegate_(windowDelegate) self._window = window self._webkit = webkit def _onLogEvent(self, event): if self._webkit: def _event(): json_str = json.dumps(event) self._webkit.evaluateJavaScript_completionHandler_('window.DQ.enqueue({})'.format(json_str), lambda a,b: None) AppHelper.callAfter(_event) def _terminate(self): AppHelper.stopEventLoop() @classmethod def run(cls, args): cls.bundle = AppKit.NSBundle.mainBundle() info = cls.bundle.localizedInfoDictionary() or cls.bundle.infoDictionary() info['CFBundleName'] = APP_NAME info['NSAppTransportSecurity'] = {'NSAllowsArbitraryLoads': Foundation.YES} info['NSRequiresAquaSystemAppearance'] = Foundation.NO cls.sharedApp = DefaultsMonitorApp.sharedApplication() cls.currentApp = AppKit.NSRunningApplication.currentApplication() # hide frod Dock # cls.sharedApp.setActivationPolicy_(AppKit.NSApplicationActivationPolicyAccessory) AppHelper.runEventLoop() if __name__ == "__main__": parser = argparse.ArgumentParser() DefaultsMonitorApp.run(parser.parse_args())