Skip to content
Snippets Groups Projects
Verified Commit ed530a89 authored by Björn Eßwein's avatar Björn Eßwein
Browse files

WIP: added devtools screencast and devtools events

TODO: implement receiving events
parent 52ace640
No related branches found
No related tags found
No related merge requests found
Showing
with 377 additions and 32 deletions
......@@ -10,8 +10,8 @@ GameObject:
m_Component:
- component: {fileID: 2974656142881083530}
- component: {fileID: 5559415116192402672}
- component: {fileID: 4531839590213585742}
- component: {fileID: 5511584970398147175}
- component: {fileID: 3767983593885104582}
- component: {fileID: 2459857651630437765}
m_Layer: 5
m_Name: BrowserView
m_TagString: Untagged
......@@ -47,7 +47,7 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8825460134736404412}
m_CullTransparentMesh: 1
--- !u!114 &4531839590213585742
--- !u!114 &3767983593885104582
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
......@@ -67,14 +67,14 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Texture: {fileID: 0}
m_Texture: {fileID: 2800000, guid: 7ee48c5fba4919649a8a6094cbead669, type: 3}
m_UVRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
--- !u!114 &5511584970398147175
--- !u!114 &2459857651630437765
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
......@@ -86,4 +86,4 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6893fa42f929f894286c6f00c40bcd06, type: 3}
m_Name:
m_EditorClassIdentifier:
targetUrl: https://google.de
headlessBrowser: 1
using ChromeDevTools;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
//using Unity.Networking.Transport;
[RequireComponent(typeof(RawImage))]
public class BrowserView : MonoBehaviour //TODO: Extends RawImage instead?
public class BrowserView : MonoBehaviour
{
private Browser browser;
private BrowserTab tab;
private RawImage rawImage;
public bool headlessBrowser = true;
//TODO: handle changed targetUrl
public string targetUrl = "https://google.de";
// Start is called before the first frame update
void Start()
private void Start()
{
rawImage = this.gameObject.GetComponent<RawImage>();
}
void OnEnable()
private void OnEnable()
{
Browser.headless = headlessBrowser;
browser = Browser.getInstance();
//StartCoroutine(GetOpenTabs());
......@@ -45,7 +46,7 @@ public class BrowserView : MonoBehaviour //TODO: Extends RawImage instead?
}
// Update is called once per frame
void Update()
private void Update()
{
}
......@@ -65,7 +66,7 @@ public class BrowserView : MonoBehaviour //TODO: Extends RawImage instead?
/**
* Close all browser windows.
*/
void OnApplicationQuit()
private void OnApplicationQuit()
{
tab.Close();
browser.Close();
......
......@@ -17,8 +17,8 @@ namespace ChromeDevTools
private static Process browserProcess;
/* browser settings */
private const string browserExecutablePath = "chrome";
private const bool headlessBrowser = false;
public static string browserExecutablePath = "chrome";
public static bool headless = true;
private const int debugPort = 9222;
/* JsonSerializer */
......@@ -63,10 +63,10 @@ namespace ChromeDevTools
{
Browser.browserProcess = new Process();
Browser.browserProcess.StartInfo.FileName = browserExecutablePath;
Browser.browserProcess.StartInfo.Arguments = $"--user-data-dir={Path.Join(Application.temporaryCachePath, "BrowserView")} --remote-debugging-port={debugPort} --remote-allow-origins=http://localhost:{debugPort}";
Browser.browserProcess.StartInfo.Arguments = $"--user-data-dir={Path.Join(Application.temporaryCachePath, "BrowserView")} --remote-debugging-port={debugPort} --remote-allow-origins=http://localhost:{debugPort} --hide-crash-restore-bubble";
// set headlessBrowser to false to see the browser window
if (headlessBrowser)
if (headless)
{
Browser.browserProcess.StartInfo.Arguments = string.Concat(Browser.browserProcess.StartInfo.Arguments, " --headless=new");
}
......
fileFormatVersion: 2
guid: 1f4e0afa13be5aa4da5b06d9d861002a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
using Newtonsoft.Json;
namespace ChromeDevTools
{
namespace Protocol
{
namespace Cast
{
}
}
}
/*
Devtools protocol.json:
{
"domain": "Cast",
"description": "A domain for interacting with Cast, Presentation API, and Remote Playback API\nfunctionalities.",
"experimental": true,
"types": [
{
"id": "Sink",
"type": "object",
"properties": [
{
"name": "name",
"type": "string"
},
{
"name": "id",
"type": "string"
},
{
"name": "session",
"description": "Text describing the current session. Present only if there is an active\nsession on the sink.",
"optional": true,
"type": "string"
}
]
}
],
"commands": [
{
"name": "enable",
"description": "Starts observing for sinks that can be used for tab mirroring, and if set,\nsinks compatible with |presentationUrl| as well. When sinks are found, a\n|sinksUpdated| event is fired.\nAlso starts observing for issue messages. When an issue is added or removed,\nan |issueUpdated| event is fired.",
"parameters": [
{
"name": "presentationUrl",
"optional": true,
"type": "string"
}
]
},
{
"name": "disable",
"description": "Stops observing for sinks and issues."
},
{
"name": "setSinkToUse",
"description": "Sets a sink to be used when the web page requests the browser to choose a\nsink via Presentation API, Remote Playback API, or Cast SDK.",
"parameters": [
{
"name": "sinkName",
"type": "string"
}
]
},
{
"name": "startDesktopMirroring",
"description": "Starts mirroring the desktop to the sink.",
"parameters": [
{
"name": "sinkName",
"type": "string"
}
]
},
{
"name": "startTabMirroring",
"description": "Starts mirroring the tab to the sink.",
"parameters": [
{
"name": "sinkName",
"type": "string"
}
]
},
{
"name": "stopCasting",
"description": "Stops the active Cast session on the sink.",
"parameters": [
{
"name": "sinkName",
"type": "string"
}
]
}
],
"events": [
{
"name": "sinksUpdated",
"description": "This is fired whenever the list of available sinks changes. A sink is a\ndevice or a software surface that you can cast to.",
"parameters": [
{
"name": "sinks",
"type": "array",
"items": {
"$ref": "Sink"
}
}
]
},
{
"name": "issueUpdated",
"description": "This is fired whenever the outstanding issue/error message changes.\n|issueMessage| is empty if there is no issue.",
"parameters": [
{
"name": "issueMessage",
"type": "string"
}
]
}
]
}*/
\ No newline at end of file
fileFormatVersion: 2
guid: b4f546ce82c7cb84b8003bc93d5cf73f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
......@@ -25,20 +25,5 @@ namespace ChromeDevTools
public DevtoolsCommandWrapper(T command) => Params = command;
}
public interface IDevtoolsCommand {}
public class DevtoolsResponseWrapper
{
public long Id { get; set; }
public JObject Result { get; set; }
}
///
/// Every devtools command response has the same id and a method as the corresponding command
///
public class DevtoolsResponseWrapper<T>: DevtoolsResponseWrapper where T: IDevtoolsResponse
{
public new T Result { get; set; }
}
public interface IDevtoolsResponse {}
}
}
\ No newline at end of file
using Newtonsoft.Json.Linq;
using System;
namespace ChromeDevTools
{
namespace Protocol
{
public class DevtoolsEventWrapper
{
public string Method { get; set; }
public JObject Params { get; set; }
}
///
/// Every devtools command response has the same id and a method as the corresponding command
///
public class DevtoolsEventWrapper<T> : DevtoolsEventWrapper where T : IDevtoolsEvent
{
public new T Params { get; set; }
}
public interface IDevtoolsEvent { }
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 399f2cceede40aa4ebe787a7f276de78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using Newtonsoft.Json.Linq;
using System;
namespace ChromeDevTools
{
namespace Protocol
{
public class DevtoolsResponseWrapper
{
public long Id { get; set; }
public JObject Result { get; set; }
}
///
/// Every devtools command response has the same id and a method as the corresponding command
///
public class DevtoolsResponseWrapper<T> : DevtoolsResponseWrapper where T : IDevtoolsResponse
{
public new T Result { get; set; }
}
public interface IDevtoolsResponse { }
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 81e639e1e8ae9084ab868731d8c5742f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using Newtonsoft.Json;
namespace ChromeDevTools
{
namespace Protocol
{
namespace Page
{
/// <summary>
/// Starts sending each frame using the `screencastFrame` event.
/// "experimental": true
///
/// No Response
/// </summary>
//[CommandResponseAttribute(typeof(StartScreencastCommandResponse))]
public class startScreencast : IDevtoolsCommand
{
/// <summary>
/// Gets or sets Image compression format (defaults to png).
/// Can be "jpeg" or "png"
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Format { get; set; }
/// <summary>
/// Gets or sets Compression quality from range [0..100].
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? Quality { get; set; }
/// <summary>
/// Maximum screenshot width.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int MaxWidth { get; set; }
/// <summary>
/// Maximum screenshot height.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int maxHeight { get; set; }
/// <summary>
/// Send every n-th frame.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int everyNthFrame { get; set; }
}
/// <summary>
/// Acknowledges that a screencast frame has been received by the frontend.
/// "experimental": true
/// </summary>
public class screencastFrameAck : IDevtoolsCommand
{
/// <summary>
/// Frame number.
/// </summary>
public int sessionId { get; set; }
}
/// <summary>
/// Stops sending each frame in the `screencastFrame`.
/// "experimental": true
/// </summary>
public class stopScreencast : IDevtoolsCommand
{ }
/// <summary>
/// Compressed image data requested by the `startScreencast`.
/// </summary>
public class screencastFrameEvent : IDevtoolsEvent
{
/// <summary>
/// Gets or sets Base64-encoded image data.
/// </summary>
public string Data { get; set; }
/// <summary>
/// Screencast frame metadata.
/// </summary>
public Types.ScreencastFrameMetadata metadata { get; set; }
/// <summary>
/// Frame number.
/// </summary>
public int sessionId { get; set; }
}
/// <summary>
/// Fired when the page with currently enabled screencast was shown or hidden `.
/// </summary>
public class screencastVisibilityChanged : IDevtoolsEvent
{
/// <summary>
/// True if the page is visible.
/// </summary>
public bool visible { get; set; }
}
namespace Types
{
/// <summary>
/// Screencast frame metadata.
/// </summary>
public class ScreencastFrameMetadata
{
/// <summary>
/// Top offset in DIP.
/// </summary>
public int offsetTop { get; set; }
/// <summary>
/// Page scale factor.
/// </summary>
public int pageScaleFactor { get; set; }
/// <summary>
/// Device screen width in DIP.
/// </summary>
public int deviceWidth { get; set; }
/// <summary>
/// Device screen height in DIP.
/// </summary>
public int deviceHeight { get; set; }
/// <summary>
/// Position of horizontal scroll in CSS pixels.
/// </summary>
public int scrollOffsetX { get; set; }
/// <summary>
/// Position of vertical scroll in CSS pixels.
/// </summary>
public int scrollOffsetY { get; set; }
/// <summary>
/// Frame swap timestamp.
/// UTC time in seconds, counted from January 1, 1970.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public long? timestamp { get; set; }
}
}
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 7f7a2406fccd8c74d89d419e99a8bf29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment