I've been looking at various options for getting rid of QtWebEngine,
and had an idea that maybe we don't actually need any C++ code at all.

Attached is a rough PoC patch that implements such a setup. If running
with SERVER_MODE == True, things are as they are now, however, if
SERVER_MODE == False then:

- We enable key generation for client validation.

- A thread is started to monitor the server for startup completion.

- Once the server is responding, a browser is launched using selenium
and pointed at the server.

- The thread continues running, periodically checking the browser is
still present. If not, it kills the server.

Downsides:

- The user knows they're running in the browser
- chromedriver/geckodriver are required (geckodriver's licence may be an issue)
- The user must have a supported browser installed.

Questions:

- Maybe we could ship a pre-built minimal browser (one that is fast
and works well and always use that?
- If we do that, then maybe we can use whatever mechanism selenium
uses to tell the browser where to navigate, as it doesn't seem to use
the command line (thus, keeping the key hidden).

Thoughts?

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
diff --git a/requirements.txt b/requirements.txt
index 071069d..fc500e0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,7 @@
 Babel==1.3
 beautifulsoup4==4.4.1
 blinker==1.3
+chromedriver_installer==0.0.6
 click==6.6
 extras==0.0.3
 fixtures==2.0.0
@@ -32,10 +33,11 @@ pyrsistent==0.11.13
 python-dateutil==2.5.0
 python-mimeparse==1.5.1
 pytz==2014.10
+selenium==3.3.1
 simplejson==3.6.5
 six>=1.9.0
 speaklater==1.3
 SQLAlchemy==1.0.14
 sqlparse==0.1.19
 Werkzeug==0.9.6
-WTForms==2.0.2
\ No newline at end of file
+WTForms==2.0.2
diff --git a/web/pgAdmin4.py b/web/pgAdmin4.py
index 6025514..23b7441 100644
--- a/web/pgAdmin4.py
+++ b/web/pgAdmin4.py
@@ -13,6 +13,13 @@ to start a web server."""
 
 import os
 import sys
+import socket
+import threading
+import time
+import uuid
+
+import requests
+from selenium import webdriver
 
 # We need to include the root directory in sys.path to ensure that we can
 # find everything we need when running in the standalone runtime.
@@ -28,6 +35,19 @@ from pgadmin import create_app
 from pgadmin.model import SCHEMA_VERSION
 config.SETTINGS_SCHEMA_VERSION = SCHEMA_VERSION
 
+def get_free_port():
+    """
+    Gets a free port number
+    Returns: The port number
+    """
+    import socket
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind(("", 0))
+    s.listen(1)
+    port = s.getsockname()[1]
+    s.close()
+    return port
+
 ##########################################################################
 # Sanity checks
 ##########################################################################
@@ -47,68 +67,107 @@ if not os.path.isfile(config.SQLITE_PATH):
 # Create the app!
 app = create_app()
 
+@app.before_first_request
+def start_browser():
+    if config.SERVER_MODE:
+        return
+
+    def go():
+        # Figure out the correct URL
+        if config.SERVER_MODE:
+            url = server_url
+        else:
+            url = '%s?key=%s' % (server_url, app.PGADMIN_KEY)
+
+        try:
+            driver = webdriver.Firefox()
+            driver.get(url)
+            app.logger.debug('Launched Firefox')
+        except:
+            try:
+                driver = webdriver.Chrome()
+                driver.get(url)
+                app.logger.debug('Launched Chrome')
+            except:
+                try:
+                    driver = webdriver.Ie()
+                    driver.get(url)
+                    app.logger.debug('Launched IE')
+                except:
+                    app.logger.error('Could not find a suitable browser. Tried 
Firefox, Chrome and IE. Exiting.')
+                    os._exit(1)
+
+        exited = False
+        while not exited:
+            app.logger.debug('Checking browser status...')
+            try:
+                driver.get_window_size()
+            except:
+                app.logger.debug('Browser quit. Exiting...')
+                driver.quit()
+                os._exit(0)
+
+            app.logger.debug('Browser running.')
+            time.sleep(5)
+
+    thread = threading.Thread(target=go)
+    thread.start()
+
+def startup_monitor():
+    def start_loop():
+        not_started = True
+        while not_started:
+            app.logger.debug('Waiting for server startup before launching 
browser...')
+            try:
+                if config.SERVER_MODE:
+                    r = requests.get('%s/misc/ping' % server_url)
+                else:
+                    r = requests.get('%s/misc/ping?key=%s' % (server_url, 
app.PGADMIN_KEY))
+
+                if r.status_code == 200:
+                    app.logger.debug('Server startup appears to be complete.')
+                    not_started = False
+            except:
+                app.logger.debug('Server not responding yet.')
+            time.sleep(2)
+
+    app.logger.debug('Started browser launcher')
+    thread = threading.Thread(target=start_loop)
+    thread.start()
+
 if config.DEBUG:
     app.debug = True
 else:
     app.debug = False
 
-# Start the web server. The port number should have already been set by the
-# runtime if we're running in desktop mode, otherwise we'll just use the
-# Flask default.
-PGADMIN_RUNTIME = False
-if 'PGADMIN_PORT' in globals():
-    app.logger.debug('Running under the desktop runtime, port: %s',
-                     globals()['PGADMIN_PORT'])
-    server_port = int(globals()['PGADMIN_PORT'])
-    PGADMIN_RUNTIME = True
-elif 'PGADMIN_PORT' in os.environ:
-    port = os.environ['PGADMIN_PORT']
-    app.logger.debug(
-        'Not running under the desktop runtime, port: %s',
-        port)
-    server_port = int(port)
-else:
-    app.logger.debug(
-        'Not running under the desktop runtime, port: %s',
-        config.DEFAULT_SERVER_PORT)
+# Start the web server. If we're in desktop mode, we'll need to set a key
+# and get a random port number
+if config.SERVER_MODE:
+    app.PGADMIN_KEY = ''
     server_port = config.DEFAULT_SERVER_PORT
-
-# Let the application save the status about the runtime for using it later.
-app.PGADMIN_RUNTIME = PGADMIN_RUNTIME
-
-# Set the key if appropriate
-if 'PGADMIN_KEY' in globals():
-    app.PGADMIN_KEY = globals()['PGADMIN_KEY']
-    app.logger.debug("Desktop security key: %s" % app.PGADMIN_KEY)
+    server_url = "http://%s:%d"; % (config.DEFAULT_SERVER, server_port)
+    app.logger.debug("Running in server mode. Please navigate to %s in your 
browser." %
+                     server_url)
 else:
-    app.PGADMIN_KEY = ''
+    app.PGADMIN_KEY = str(uuid.uuid4())
+    server_port = get_free_port()
+    server_url = "http://%s:%d"; % (config.DEFAULT_SERVER, server_port)
+    app.logger.debug("Running in desktop mode. Please navigate to %s?key=%s in 
your browser." %
+                     (server_url, app.PGADMIN_KEY))
 
 # Output a startup message if we're not under the runtime and startup.
 # If we're under WSGI, we don't need to worry about this
 if __name__ == '__main__':
-    if not PGADMIN_RUNTIME:
-        print("Starting %s. Please navigate to http://%s:%d in your browser." %
-              (config.APP_NAME, config.DEFAULT_SERVER, server_port))
-        sys.stdout.flush()
-    else:
-        # For unknown reason the Qt runtime does not pass the environment
-        # variables (i.e. PYTHONHOME, and PYTHONPATH), to the Python
-        # sub-processes, leading to failures executing background processes.
-        #
-        # This has been observed only on windows. On *nix systems, it is likely
-        # picking the system python environment, which is good enough to run
-        # the process-executor.
-        #
-        # Setting PYTHONHOME launch them properly.
-        from pgadmin.utils import IS_WIN
-        if IS_WIN:
-            os.environ['PYTHONHOME'] = sys.prefix
-
     try:
+        # Start the browser
+        if not config.SERVER_MODE:
+            startup_monitor()
+
+        # Start the app
         app.run(
             host=config.DEFAULT_SERVER,
             port=server_port,
-            use_reloader=((not PGADMIN_RUNTIME) and app.debug),
+            use_reloader=app.debug,
             threaded=config.THREADED_MODE
         )
     except IOError:
diff --git a/web/pgadmin/misc/bgprocess/processes.py 
b/web/pgadmin/misc/bgprocess/processes.py
index ad6d0ff..41975d7 100644
--- a/web/pgadmin/misc/bgprocess/processes.py
+++ b/web/pgadmin/misc/bgprocess/processes.py
@@ -228,44 +228,7 @@ class BatchProcess(object):
             if interpreter is None:
                 interpreter = which(u'python.exe', paths)
 
-            if interpreter is None and current_app.PGADMIN_RUNTIME:
-                # We've faced an issue with Windows 2008 R2 (x86) regarding,
-                # not honouring the environment variables set under the Qt
-                # (e.g. runtime), and also setting PYTHONHOME same as
-                # sys.executable (i.e. pgAdmin4.exe).
-                #
-                # As we know, we're running it under the runtime, we can assume
-                # that 'venv' directory will be available outside of 'bin'
-                # directory.
-                #
-                # We would try out luck to find python executable based on that
-                # assumptions.
-                bin_path = os.path.dirname(sys.executable)
-
-                venv = os.path.realpath(
-                    os.path.join(bin_path, u'..\\venv')
-                )
-
-                interpreter = which(u'pythonw.exe', [venv])
-                if interpreter is None:
-                    interpreter = which(u'pythonw.exe', [venv])
-
-                if interpreter is not None:
-                    # Our assumptions are proven right.
-                    # Let's append the 'bin' directory to the PATH environment
-                    # variable. And, also set PYTHONHOME environment variable
-                    # to 'venv' directory.
-                    os.environ['PATH'] = bin_path + ';' + os.environ['PATH']
-                    os.environ['PYTHONHOME'] = venv
-        else:
-            # Let's not use sys.prefix in runtime.
-            # 'sys.prefix' is not identified on *nix systems for some unknown
-            # reason, while running under the runtime.
-            # We're already adding '<installation path>/pgAdmin 4/venv/bin'
-            # directory in the PATH environment variable. Hence - it will
-            # anyway be the redundant value in paths.
-            if not current_app.PGADMIN_RUNTIME:
-                paths.insert(0, os.path.join(u(sys.prefix), u'bin'))
+            paths.insert(0, os.path.join(u(sys.prefix), u'bin'))
             interpreter = which(u'python', paths)
 
         p = None
diff --git a/web/pgadmin/tools/datagrid/__init__.py 
b/web/pgadmin/tools/datagrid/__init__.py
index fe2d3e5..d154440 100644
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ b/web/pgadmin/tools/datagrid/__init__.py
@@ -185,7 +185,6 @@ def panel(trans_id, is_query_tool, editor_title):
     return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id,
                            is_query_tool=is_query_tool,
                            editor_title=editor_title, script_type_url=sURL,
-                           is_desktop_mode=app.PGADMIN_RUNTIME,
                            is_linux=is_linux_platform,
                            is_new_browser_tab=new_browser_tab)
 
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html 
b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 1d71b21..524bd6d 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -6,13 +6,6 @@
 {% block body %}
 <style>
     body {padding: 0px;}
-    {% if is_desktop_mode and is_linux %}
-    .alertify .ajs-dimmer,.alertify .ajs-modal{-webkit-transform: none;}
-    .alertify-notifier{-webkit-transform: none;}
-    .alertify-notifier .ajs-message{-webkit-transform: none;}
-    .alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
-    .sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
-    {% endif %}
 </style>
 <div id="main-editor_panel">
     <div id="fetching_data" class="wcLoadingIconContainer 
sql-editor-busy-fetching hide">
diff --git a/web/pgadmin/tools/debugger/__init__.py 
b/web/pgadmin/tools/debugger/__init__.py
index fa414ce..9b65937 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -329,7 +329,6 @@ def direct_new(trans_id):
         function_name=obj['function_name'],
         uniqueId=trans_id,
         debug_type=debug_type,
-        is_desktop_mode=current_app.PGADMIN_RUNTIME,
         is_linux=is_linux_platform,
         stylesheets=[url_for('debugger.static', filename='css/debugger.css')]
     )
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html 
b/web/pgadmin/tools/debugger/templates/debugger/direct.html
index 297c362..2af9a0a 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/direct.html
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -17,16 +17,6 @@ console.log(err);
 }
 {% endblock %}
 {% block body %}
-{% if is_desktop_mode and is_linux %}
-<style>
-    body
-    .alertify .ajs-dimmer,.alertify .ajs-modal{-webkit-transform: none;}
-    .alertify-notifier{-webkit-transform: none;}
-    .alertify-notifier .ajs-message{-webkit-transform: none;}
-    .alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
-    .debugger-container .wcLoadingIcon.fa-pulse{-webkit-animation: none;}
-</style>
-{% endif %}
 <nav class="navbar-inverse navbar-fixed-top">
     <div id="btn-toolbar" class="btn-toolbar pg-prop-btn-group" role="toolbar" 
aria-label="">
         <div class="btn-group" role="group" aria-label="">
diff --git a/web/regression/requirements.txt b/web/regression/requirements.txt
index f644c12..eed11f9 100644
--- a/web/regression/requirements.txt
+++ b/web/regression/requirements.txt
@@ -1,5 +1,3 @@
-chromedriver_installer==0.0.6
-selenium==3.3.1
 testscenarios==0.5.0
 testtools==2.0.0
 traceback2==1.4.0
-- 
Sent via pgadmin-hackers mailing list (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to