Initial commit

This repository holds code for a Chrome extension which shows
instructions for manual testing of touch panels, collects
touch events, and sends touch events over a WebSocket.

This is an unchanged copy of software released by Optofidelity OY.

Change-Id: I6a990ef1daca70ef5712941e267156c3dd3204b4
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/optofidelity_chrome_touch_client/+/2023316
Reviewed-by: Harry Cutts <hcutts@chromium.org>
Commit-Queue: Sean O'Brien <seobrien@chromium.org>
Tested-by: Sean O'Brien <seobrien@chromium.org>
diff --git a/README.chromium b/README.chromium
new file mode 100644
index 0000000..e3f8fad
--- /dev/null
+++ b/README.chromium
@@ -0,0 +1,4 @@
+DESCRIPTION="Chrome extension to show manual touch validation instructions"
+LOCAL_GIT_REPO="https://chromium.googlesource/com/chromiumos/third_party/optofidelity_chrome_touch_client
+LOCAL_BUGSDB="https://crbug.com"
+LICENSE="4-clause BSD"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..db48faa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+### Chrome Extension
+
+To enable in Chrome browser:
+
+1. navigate to chrome://extensions
+2. enable "developer mode"
+3. click "load unpacked" and select chrome_plugin directory
+
+#### To create package:
+
+1. navigate to chrome://extensions
+2. you have to have the Touch Client plugin installed in developer mode
+3. click "Details" in Touch Client plugin
+4. at the top bar click "Pack Extension"
+
+
+#### Usage:
+
+When installed, the chrome plugin will show as a button at the toolbar.
+The button looks like Optofidelity logo; red, green and blue squares.
+
+1. Open settings by right-clicking the button.
+2. Set TPPT server's ip-address, port 50009 and DUT name (default: Dut1)
+3. Make sure that you have started the TPPT server.
+4. Left-click the toolbar button, this should open a new page with "full screen" button on it. This will also open the connection to the TPPT server.
+5. Follow the instructions from the TPPT server.
+
+If the connection is lost, refresh the page. Refreshing the page will create new connection to the server.
+
+
diff --git a/chrome_plugin/application.mjs b/chrome_plugin/application.mjs
new file mode 100644
index 0000000..27caf2d
--- /dev/null
+++ b/chrome_plugin/application.mjs
@@ -0,0 +1,196 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+export function Application( dut_name )
+    {
+    //
+    // Application is the main logic of a websocket client.
+    // Websocket Communicator needs an application instance to run.
+    // Communicator will parse incoming messages and run functions from Application.
+    // Functions take in command name and (json-formatted) parameters, return (json-formatted) response.
+    //
+
+    // register commands (this is the API for the application)
+    this.name = dut_name;
+
+    this.commands = {
+                    "touches": this.cmd_touches.bind(this),
+                    "draw_image": this.cmd_draw_image.bind(this),
+                    "get_info": this.cmd_get_info.bind(this)
+                    };
+
+    // register event listeners for touch recording.
+    var t = document.getElementById("infotainment")
+
+    t.ontouchstart = this.mousedown.bind(this);
+    t.ontouchmove = this.mousemove.bind(this);
+    t.ontouchend = this.mouseup.bind(this);
+
+    t.onmousedown = this.mousedown.bind(this);
+    t.onmousemove = this.mousemove.bind(this);
+    t.onmouseup = this.mouseup.bind(this);
+
+    // set up touches array
+    this.touches = [];
+
+    // constants used in cmd_touches response. For each touch, store [touch_type, x, y]
+    this.ACTION_DOWN = 0;
+    this.ACTION_UP   = 1;
+    this.ACTION_MOVE = 2;
+    }
+
+Application.prototype.cmd_touches = function(params)
+    {
+    //
+    // touches command
+    //
+    var t = [];
+    for( var i=0; i<this.touches.length; i++ )
+        {
+        var p = this.touches[ i ];
+        t.push( p );
+        }
+    this.touches = [];
+    return {"fields": ["action", "x", "y", "time", "id"], "touches": t};
+    }
+
+Application.prototype.cmd_draw_image = function(params)
+    {
+    //
+    // draw image command
+    //
+    // params : dict {'image_data': base64 encoded image data (jpeg, png, etc.)}
+
+    var image_data = params["image"];
+
+    // resize canvas if needed
+    if( infotainment.width != infotainment.clientWidth )
+        {
+        infotainment.width = infotainment.clientWidth;
+        }
+
+    if( infotainment.height != infotainment.clientHeight )
+        {
+        infotainment.height = infotainment.clientHeight;
+        }
+
+    var img = new Image();
+    img.x0 = 0;
+    img.y0 = 0;
+    img.x1 = infotainment.clientWidth;
+    img.y1 = infotainment.clientHeight;
+    img.onload = this.imageLoaded.bind(this);
+    img.src = "data:image/png;base64," + image_data;
+
+    return ""
+    }
+
+Application.prototype.imageLoaded = function(e)
+    {
+    //
+    // The async part of the draw image command.
+    //
+    var img = e.target;
+    var c = infotainment.getContext( "2d" );
+
+    c.drawImage( img, img.x0, img.y0, img.x1-img.x0, img.y1-img.y0 );
+    }
+
+Application.prototype.cmd_get_info = function(params)
+    {
+    //
+    // Get info command. Return all needed info of the device under test.
+    //
+    var info = {
+               "dut_name": this.name,
+               "width": infotainment.clientWidth,
+               "height": infotainment.clientHeight,
+               "pixel_ratio": devicePixelRatio,
+               "display_resolution": {"width": infotainment.clientWidth, "height": infotainment.clientHeight }
+               };
+    return info;
+    }
+
+Application.prototype.mousedown = function(e)
+    {
+    e.preventDefault();
+
+    var ct = e.changedTouches;
+    if( ct )
+        {
+        // touch events
+        for( var i=0; i<ct.length; i++ )
+            {
+            var t = ct[ i ];
+            this.touches.push([this.ACTION_DOWN, t.pageX, t.pageY, Math.floor(e.timeStamp), t.identifier]);
+            }
+        }
+    else
+        {
+        // mouse events
+        this.touches.push([this.ACTION_DOWN, e.pageX, e.pageY, Math.floor(e.timeStamp), 0]);
+        }
+    }
+
+Application.prototype.mousemove = function(e)
+    {
+    e.preventDefault();
+
+    var ct = e.changedTouches;
+    if( ct )
+        {
+        // touch events
+        for( var i=0; i<ct.length; i++ )
+            {
+            var t = ct[ i ];
+            this.touches.push([this.ACTION_MOVE, t.pageX, t.pageY, Math.floor(e.timeStamp), t.identifier]);
+            }
+        }
+    else
+        {
+        // mouse events
+        this.touches.push([this.ACTION_MOVE, e.pageX, e.pageY, Math.floor(e.timeStamp), 0]);
+        }
+    }
+
+Application.prototype.mouseup = function(e)
+    {
+    e.preventDefault();
+
+    var ct = e.changedTouches;
+    if( ct )
+        {
+        // touch events
+        for( var i=0; i<ct.length; i++ )
+            {
+            var t = ct[ i ];
+            this.touches.push([this.ACTION_UP, t.pageX, t.pageY, Math.floor(e.timeStamp), t.identifier]);
+            }
+        }
+    else
+        {
+        // mouse events
+        this.touches.push([this.ACTION_UP, e.pageX, e.pageY, Math.floor(e.timeStamp), 0]);
+        }
+    }
+
diff --git a/chrome_plugin/background.js b/chrome_plugin/background.js
new file mode 100644
index 0000000..a2c0755
--- /dev/null
+++ b/chrome_plugin/background.js
@@ -0,0 +1,28 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// enable functionality on every tab
+chrome.tabs.onUpdated.addListener(function(id, info, tab)
+    {
+    chrome.pageAction.show(tab.id);
+    });
+
diff --git a/chrome_plugin/communicator.mjs b/chrome_plugin/communicator.mjs
new file mode 100644
index 0000000..fd107f4
--- /dev/null
+++ b/chrome_plugin/communicator.mjs
@@ -0,0 +1,144 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+Websocket Communicator.
+(C) 2019 Optofidelity.
+
+Usage:
+    var host = "127.0.0.1";
+    var port = 50009;
+    var dut_name = "DUT1";
+    var application = new Application( dut_name );
+    var communicator = new Communicator( host, port, application );
+    communicator.start()
+
+Application instance must have dictionary called "commands"
+    commands must have key for every command in API.
+    commands must have functions for every key, taking in (json-originated) parameters.
+    command functions must return a (json-formattable) value.
+*/
+
+export function Communicator( host, port, application )
+    {
+    //
+    // port: Websocket port of the server.
+    // application: Application instance.
+    //
+    console.log("Communicator init");
+    this.host = host;
+    this.port = port;
+    this.application = application;
+    this.buffer = "";
+    this.socket = null;
+    this.commands = {};
+    }
+
+Communicator.prototype.handleCmd = function( cmd )
+    {
+    //
+    // Handle a command
+    // IN: "command_name" + " " + "json-formatted parameters" + "\n"
+    // OUT:
+    //      "ok " + json formatted response
+    //      "error " + error message string.
+
+    if( cmd == "" ) return;
+
+    var params = [];
+    var p = cmd.indexOf(' ');
+    if( p >= 0 )
+        {
+        params = cmd.substr( p + 1 )
+        cmd = cmd.substr( 0, p );
+        }
+    cmd = cmd.toLowerCase()
+
+    var rv = "";
+
+    try
+        {
+        var command = this.application.commands[ cmd ];
+        if( command )
+            {
+            if( params.length )
+                {
+                params = JSON.parse( params );
+                }
+            var response = command( params );
+            response = JSON.stringify( response );
+            rv = "ok " + response;
+            }
+
+        else
+            {
+            rv = 'error';
+            }
+        }
+    catch( err )
+        {
+        console.log( err );
+        rv = 'error ' + err;
+        }
+    return rv;
+    }
+
+Communicator.prototype.start = function()
+    {
+    var s = new WebSocket("ws://" + this.host + ":" + this.port + "/");
+    s.onopen = function(e)
+        {
+        console.log("opened");
+        this.buffer = "";
+        }
+    s.onclose = function(e) { console.log("closed"); }
+    s.onmessage = this.onmessage.bind( this );
+    this.socket = s;
+    };
+
+Communicator.prototype.onmessage = function(e)
+    {
+    this.buffer += e.data;
+
+    // messages split with newline
+
+    var p = this.buffer.search("\n");
+    if( p < 0 )
+        {
+        return;
+        }
+    var msg = this.buffer.substr( 0, p );
+    this.buffer = this.buffer.substr( p+1 );
+
+    var cmds = msg.trim().split('\n');
+    var rv = "";
+    for( var i=0; i<cmds.length; i++ )
+        {
+        var cmd = cmds[ i ];
+        rv += this.handleCmd( cmd );
+        rv += '\n';
+        }
+
+    // always respond
+    this.socket.send(rv)
+    }
+
diff --git a/chrome_plugin/icons/optofidelity_icon_128.png b/chrome_plugin/icons/optofidelity_icon_128.png
new file mode 100644
index 0000000..7ffab67
--- /dev/null
+++ b/chrome_plugin/icons/optofidelity_icon_128.png
Binary files differ
diff --git a/chrome_plugin/icons/optofidelity_icon_16.png b/chrome_plugin/icons/optofidelity_icon_16.png
new file mode 100644
index 0000000..6f8070a
--- /dev/null
+++ b/chrome_plugin/icons/optofidelity_icon_16.png
Binary files differ
diff --git a/chrome_plugin/icons/optofidelity_icon_32.png b/chrome_plugin/icons/optofidelity_icon_32.png
new file mode 100644
index 0000000..be83fd6
--- /dev/null
+++ b/chrome_plugin/icons/optofidelity_icon_32.png
Binary files differ
diff --git a/chrome_plugin/icons/optofidelity_icon_48.png b/chrome_plugin/icons/optofidelity_icon_48.png
new file mode 100644
index 0000000..80125e7
--- /dev/null
+++ b/chrome_plugin/icons/optofidelity_icon_48.png
Binary files differ
diff --git a/chrome_plugin/manifest.json b/chrome_plugin/manifest.json
new file mode 100644
index 0000000..02eb990
--- /dev/null
+++ b/chrome_plugin/manifest.json
@@ -0,0 +1,42 @@
+  {
+    "name": "Touch Client",
+    "version": "1.0",
+    "description": "TnT compatible touch client",
+    "manifest_version": 2,
+    "options_page": "options.html",
+
+    "background":
+      {
+      "scripts": ["background.js"],
+      "persistent": false
+      },
+
+    "page_action":
+      {
+      "default_popup": "popup.html",
+      "default_icon":
+        {
+        "16": "icons/optofidelity_icon_16.png",
+        "32": "icons/optofidelity_icon_32.png",
+        "48": "icons/optofidelity_icon_48.png",
+        "128": "icons/optofidelity_icon_128.png"
+        }
+      },
+
+    "icons":
+      {
+      "16": "icons/optofidelity_icon_16.png",
+      "32": "icons/optofidelity_icon_32.png",
+      "48": "icons/optofidelity_icon_48.png",
+      "128": "icons/optofidelity_icon_128.png"
+      },
+
+    "permissions":
+      [
+      "storage",
+      "tabs",
+      "notifications",
+      "http://*/",
+      "https://*/"
+      ]
+  }
\ No newline at end of file
diff --git a/chrome_plugin/options.html b/chrome_plugin/options.html
new file mode 100644
index 0000000..16aa385
--- /dev/null
+++ b/chrome_plugin/options.html
@@ -0,0 +1,71 @@
+<!--
+
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-->
+
+<!DOCTYPE html>
+  <html>
+    <head>
+      <style>
+        button 
+            {
+            height: 30px;
+            width: 30px;
+            outline: none;
+            margin: 10px;
+            }
+
+        .option_label
+            {
+            width: 200px;
+            height: 18px;
+            margin-left: -100px;
+            left: 50%;
+            margin-bottom: 0px;
+            position: relative;
+            text-align: center;
+            margin-top: 10px;
+            }
+            
+        .option_input
+            {
+            width: 200px;
+            height: 44px;
+            margin-left: -100px;
+            left: 50%;
+            position: relative;
+            background-color: white;
+            border-radius: 7px;
+            margin-bottom: 10px;
+            text-align: center;
+            }
+        
+      </style>
+    </head>
+    <body>
+      <div>
+        <h1><center>Touch Client Settings</center></h1>
+      </div>
+      <div id="buttonDiv"></div>
+    </body>
+    <script src="options.js"></script>
+  </html>
\ No newline at end of file
diff --git a/chrome_plugin/options.js b/chrome_plugin/options.js
new file mode 100644
index 0000000..549ff86
--- /dev/null
+++ b/chrome_plugin/options.js
@@ -0,0 +1,68 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+let page = document.getElementById('buttonDiv');
+
+function constructOptions()
+    {
+    function createOption( label, type, variable_name )
+        {
+        let group = document.createElement( "div" );
+
+        let labelDiv = document.createElement( "div" );
+        labelDiv.innerHTML = label;
+        labelDiv.className = "option_label";
+
+        group.append( labelDiv );
+
+        let inputDiv = document.createElement( "input" );
+        inputDiv.setAttribute( "input-type", type );
+        inputDiv.className = "option_input";
+
+        group.appendChild( inputDiv );
+
+        group.style.background = "linear-gradient(transparent, rgba(0,0,0,0.1))";
+
+        page.appendChild( group );
+
+        chrome.storage.local.get([variable_name], function(result)
+            {
+            console.log( "hoi", result );
+            inputDiv.value = result[variable_name];
+            });
+
+        inputDiv.onchange = function( e )
+            {
+            chrome.storage.local.set({[variable_name]: inputDiv.value}, function()
+                {
+                console.log('Value of ' + variable_name + ' is set to ' + inputDiv.value);
+                });
+            }
+
+        }
+
+    createOption( "Server IP address", "text", "host" );
+    createOption( "Server IP port", "number", "port" );
+    createOption( "DUT name", "text", "name" );
+    }
+constructOptions();
\ No newline at end of file
diff --git a/chrome_plugin/popup.html b/chrome_plugin/popup.html
new file mode 100644
index 0000000..fe5e0ae
--- /dev/null
+++ b/chrome_plugin/popup.html
@@ -0,0 +1,28 @@
+<!--
+
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-->
+
+<!DOCTYPE html>
+<html>
+<script src="popup.js"></script>
+</html>
\ No newline at end of file
diff --git a/chrome_plugin/popup.js b/chrome_plugin/popup.js
new file mode 100644
index 0000000..1999f51
--- /dev/null
+++ b/chrome_plugin/popup.js
@@ -0,0 +1,23 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+chrome.tabs.create({"url": "testapp.html"});
diff --git a/chrome_plugin/testapp.html b/chrome_plugin/testapp.html
new file mode 100644
index 0000000..9542c62
--- /dev/null
+++ b/chrome_plugin/testapp.html
@@ -0,0 +1,69 @@
+<!--
+
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>testapp</title>
+</head>
+
+
+<style>
+    body
+        {
+        width: 100%;
+        height: 100%;
+        margin: 0;
+        padding: 0;
+        border: 0;
+        }
+
+    .infotainment
+        {
+        width: 100%;
+        height: 100%;
+        position: fixed;
+        left: 0px;
+        top: 0px;
+        }
+
+    .fullscreenbutton
+        {
+        position: fixed;
+        left: 0px;
+        top: 0px;
+        }
+</style>
+
+
+
+<body>
+    <canvas id="infotainment" class="infotainment"></canvas>
+    <button id="fullscreenbutton" class="fullscreenbutton">Full Screen</button>
+</body>
+
+<script src="testapp.js" type="module"></script>
+
+</html>
\ No newline at end of file
diff --git a/chrome_plugin/testapp.js b/chrome_plugin/testapp.js
new file mode 100644
index 0000000..4aeacc2
--- /dev/null
+++ b/chrome_plugin/testapp.js
@@ -0,0 +1,113 @@
+/*
+Copyright (c) 2019, OptoFidelity OY
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
+    4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+function Start()
+    {
+    var canvas = document.getElementById( "infotainment" );
+    var b = document.getElementById("fullscreenbutton")
+
+    b.onclick = function(e)
+        {
+        document.documentElement.webkitRequestFullscreen();
+        };
+
+    canvas.onmousemove = function( e )
+        {
+        if( e.button == 0 )
+            {
+            if( canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight )
+                {
+                canvas.width = canvas.clientWidth;
+                canvas.height = canvas.clientHeight;
+                }
+
+            var c = canvas.getContext( "2d" );
+
+
+            var x = e.clientX;
+            var y = e.clientY;
+            c.fillRect( x-2, y-2, 4, 4 );
+            }
+        }
+    }
+
+
+// Start();
+
+import { Application } from './application.mjs';
+import { Communicator } from './communicator.mjs';
+
+
+function DoStart()
+    {
+    function Starter()
+        {
+        this.host = null;
+        this.port = null;
+        this.name = null;
+
+        chrome.storage.local.get(["host"], this.gethost.bind(this));
+        chrome.storage.local.get(["port"], this.getport.bind(this));
+        chrome.storage.local.get(["name"], this.getname.bind(this));
+        }
+
+    Starter.prototype.gethost = function( result )
+        {
+        this.host = result["host"]
+        console.log( "got host " + this.host );
+        this.tryStart();
+        }
+
+    Starter.prototype.getport = function( result )
+        {
+        this.port = result["port"]
+        console.log( "got port " + this.port );
+        this.tryStart();
+        }
+
+    Starter.prototype.getname = function( result )
+        {
+        this.name = result["name"]
+        console.log( "got name " + this.name );
+        this.tryStart();
+        }
+
+    Starter.prototype.tryStart = function()
+        {
+        if( this.host && this.port && this.name )
+            {
+            var testapp_application = new Application(this.name);
+            var testapp_communicator = new Communicator( this.host, this.port, testapp_application );
+            testapp_communicator.start();
+            }
+        }
+
+    var b = document.getElementById("fullscreenbutton")
+    b.onclick = function(e)
+        {
+        document.documentElement.webkitRequestFullscreen();
+        };
+
+    var testapp_starter = new Starter();
+    }
+
+DoStart();
\ No newline at end of file