Expanding on the answers here for posterity as I have been struggling with this today.
To launch the Chrome DevTools window correctly and automatically the following sequence is needed.
- To kill any running instances of your app on the TV run
sdb shell 0 was_kill SomePackage.Identifier
- To start your app in debug mode on TV run
sdb shell 0 debug SomePackage.Identifier
- Read the port number from output (see python script below), let's assume it was
... successfully launched pid = 15900 with debug 1 port: 37846
- Remove any forwarded port configurations
sdb forward --remove tcp:37846
- Create new port forwarding
sdb forward tcp:37846 tcp:37846
- Find the correct url for the devtools window by parsing the JSON returned by
http://localhost:37846/json
, (key of interest is json[0].devtoolsFrontendUrl
)
- launch Chrome with
http://localhost:37846/{devtoolsFrontendUrl}
I wrote a small Python script that I run to start my debugging session, this does it all and opens a chrome window for me to debug. Just like Tizen Studio.
python debug_on_tv.py --appid SomePackage.Identifier --chrome "C:\Program Files\Google\Chrome\Application\chrome.exe"
#!/usr/bin/env python
# coding=utf-8
import argparse # Command-line argument parser
import subprocess
import requests, json
import tempfile
def parseArguments():
parser = argparse.ArgumentParser()
parser.add_argument("--appid", help="The identifier of the tizen app",
type=str)
parser.add_argument("--chrome", help="path to chrome executable",
type=str)
return parser.parse_args()
##
## Note: reverse engineer the shell 0 commands from
## https://github.com/Samsung/Wits/blob/c18cf03eced66aa66c3603c94fccf4c14d193f00/lib/appLaunchHelper.js
def runMain():
try:
# Construct the argument parser for the commandline
args = parseArguments()
# Force a UTF8 environment for the subprocess so that files with non-ascii characters are read correctly
# for this to work we must not use the universal line endings parameter
my_env = os.environ
my_env['PYTHONIOENCODING'] = 'utf-8'
# Start by killing any processes already running
prog_args = ['sdb']
prog_args.append('shell')
prog_args.append('0')
prog_args.append('was_kill')
prog_args.append(args.appid)
subprocess.run(prog_args)
# Start the app in debug mode
prog_args = ['sdb']
prog_args.append('shell')
prog_args.append('0')
prog_args.append('debug')
prog_args.append(args.appid)
ret = subprocess.Popen(prog_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, env=my_env)
debugport = 0
try:
while True:
try:
line = ret.stdout.readline()
if not line:
break
line = line.strip()
# Searching for >> ... successfully launched pid = 10422 with debug 1 port: 34187
if 'with debug 1 port' in line:
debugport = line[line.rfind(':')+1:].strip()
except UnicodeDecodeError:
continue # Ignore all unicode errors, don't care!
# Ensure that the return code was ok before continuing
# Check if child process has terminated. Set and return returncode attribute. Otherwise, returns None.
retcode = ret.poll()
while retcode is None:
retcode = ret.poll()
except KeyboardInterrupt:
ret.terminate()
raise
# Got the debug port, kill any existing connections on this port before establishing a new one
prog_args = ['sdb']
prog_args.append('forward')
prog_args.append('--remove')
prog_args.append('tcp:{0}'.format(debugport))
subprocess.run(prog_args)
# Got the Debug port, now forward it
prog_args = ['sdb']
prog_args.append('forward')
prog_args.append('tcp:{0}'.format(debugport))
prog_args.append('tcp:{0}'.format(debugport))
subprocess.run(prog_args)
# Extract the complete debug path from the remote view
# Idea from: https://github.com/Samsung/webIDE-common-tizentv/blob/dev/lib/appLauncher.js#L141C42-L141C47
responseJson = json.loads(requests.get('http://localhost:{0}/json'.format(debugport)).text)
devUrl = responseJson[0]['devtoolsFrontendUrl']
# Start chrome,
# see cmd line params: https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts
prog_args = [args.chrome]
prog_args.append('--new-window')
prog_args.append('--no-first-run')
prog_args.append('--disable-setuid-sandbox')
prog_args.append('--hide-crash-restore-bubble')
prog_args.append('--activate-on-launch')
prog_args.append('--no-default-browser-check')
prog_args.append('--allow-file-access-from-files')
prog_args.append('--disable-web-security') # This is critical, otherwise we cannot do unsecure CORS
prog_args.append('--disable-features=Translate,OptimizationHints,MediaRouter')
prog_args.append('--disable-extensions')
prog_args.append('--disable-component-extensions-with-background-pages')
prog_args.append('--disable-background-networking')
prog_args.append('--disable-component-update')
prog_args.append('--disable-client-side-phishing-detection')
prog_args.append('--disable-sync')
prog_args.append('--metrics-recording-only')
prog_args.append('--disable-default-apps')
prog_args.append('--disable-backgrounding-occluded-windows')
prog_args.append('--disable-renderer-backgrounding')
prog_args.append('--disable-background-timer-throttling')
prog_args.append('--disable-ipc-flooding-protection')
prog_args.append('--password-store=basic')
prog_args.append('--force-fieldtrials=*BackgroundTracing/default/')
prog_args.append('--use-mock-keychain')
prog_args.append('--enable-blink-features=ShadowDOMV0,CustomElementsV0,HTMLImports') # This is done by WITS, so we do it as well
prog_args.append('--user-data-dir={0}'.format(tempfile.TemporaryDirectory().name)) # To open a new window, # Create a temporary directory for the dummy chrome profile
prog_args.append('--auto-open-devtools-for-tabs')
prog_args.append('http://localhost:{0}{1}'.format(debugport,devUrl))
subprocess.run(prog_args)
finally:
print("Done")
# If the script file is called by itself then execute the main function
if __name__ == '__main__':
runMain()