Skip to content
Snippets Groups Projects
Unverified Commit 0b1eaf92 authored by Bjoern Esswein's avatar Bjoern Esswein
Browse files

WIP: implement DomNodeWrapper

parent d7ba8822
No related branches found
No related tags found
No related merge requests found
Showing with 736 additions and 97 deletions
......@@ -14,7 +14,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
{
/* singleton */
private static Browser instance;
private static Process browserProcess;
private Process browserProcess;
/* browser settings */
public static string browserExecutablePath = "chrome";
......@@ -37,7 +37,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
*/
public static Browser getInstance()
{
if (instance == null)
if (instance == null || instance.browserProcess.HasExited)
{
instance = new Browser();
instance.launchBrowser();
......@@ -64,31 +64,45 @@ namespace bessw.Unity.WebView.ChromeDevTools
private void launchBrowser()
{
// allow only one instance of chrome
if (Browser.browserProcess == null || Browser.browserProcess.HasExited)
{
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} --hide-crash-restore-bubble";
if (browserProcess == null || browserProcess.HasExited)
{
browserProcess = new Process();
browserProcess.StartInfo.FileName = browserExecutablePath;
browserProcess.StartInfo.Arguments = String.Join(" ", new []{
$"--user-data-dir={Path.Join(Application.temporaryCachePath, "BrowserView")}",
$"--remote-debugging-port={debugPort}",
$"--remote-allow-origins=http://localhost:{debugPort}",
"--hide-crash-restore-bubble",
"--disable-first-run-ui",
"--no-first-run"
});
// set headlessBrowser to false to see the browser window
if (headless)
{
Browser.browserProcess.StartInfo.Arguments = string.Concat(Browser.browserProcess.StartInfo.Arguments, " --headless=new");
browserProcess.StartInfo.Arguments = string.Concat(browserProcess.StartInfo.Arguments, " --headless=new");
}
else
{
Browser.browserProcess.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
browserProcess.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
}
Browser.browserProcess.Start();
UnityEngine.Debug.Log($"launched '{Browser.browserProcess.StartInfo.FileName} {Browser.browserProcess.StartInfo.Arguments}'");
// register an error handler
browserProcess.ErrorDataReceived += (sender, e) => UnityEngine.Debug.LogError($"Browser Error: {e.Data} ExitCode: {browserProcess.ExitCode}");
browserProcess.Exited += (sender, e) => UnityEngine.Debug.LogError($"Browser Exited, ExitCode: {browserProcess.ExitCode}");
browserProcess.Start();
if (browserProcess.HasExited) {
UnityEngine.Debug.LogError("Failed to start browser");
}
UnityEngine.Debug.Log($"launched '{browserProcess.StartInfo.FileName} {browserProcess.StartInfo.Arguments}'");
}
}
/**
* send web request to the devTools API
*/
private IEnumerator DevToolsApiRequest(bool isPUT, string apiAddress, System.Action<string> callback)
private IEnumerator DevToolsApiRequest(bool isPUT, string apiAddress, Action<string> callback)
{
UnityEngine.Debug.Log($"DevTools api Request: {apiAddress}");
UnityWebRequest webRequest;
......@@ -114,7 +128,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
}
}
public IEnumerator OpenNewTab(string targetUrl, System.Action<BrowserTab> callback)
public IEnumerator OpenNewTab(string targetUrl, Action<BrowserTab> callback)
{
yield return DevToolsApiRequest(true, $"/json/new?{targetUrl}", (response) =>
{
......
......@@ -3,6 +3,7 @@ using bessw.Unity.WebView.ChromeDevTools.Protocol.Page;
using bessw.Unity.WebView.ChromeDevTools.Protocol.Target;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
......@@ -12,19 +13,31 @@ namespace bessw.Unity.WebView.ChromeDevTools
public class BrowserTab
{
private PageTargetInfo pageTarget;
private DevtoolsProtocolHandler devtools;
public DevtoolsProtocolHandler devtools;
/// <summary>
/// width and height of the brower device
/// </summary>
public Vector2Int size { get; private set; }
public Dictionary<int, DomNodeWrapper> domNodes = new Dictionary<int, DomNodeWrapper>();
public BrowserTab(PageTargetInfo pageTarget)
{
this.pageTarget = pageTarget;
Debug.Log($"tab WebSocket: '{pageTarget.WebSocketDebuggerUrl}'");
this.devtools = new DevtoolsProtocolHandler(new DevtoolsWebsocket(pageTarget.WebSocketDebuggerUrl));
// register DOM event handlers
this.devtools.DOM_AttributeModifiedEventHandler += (attributeModifiedEvent) => DomNodeWrapper.onAttributeModified(this, attributeModifiedEvent);
this.devtools.DOM_AttributeRemovedEventHandler += (attributeRemovedEvent) => DomNodeWrapper.onAttributeRemoved(this, attributeRemovedEvent);
this.devtools.DOM_CharacterDataModifiedEventHandler += (characterDataModifiedEvent) => DomNodeWrapper.onCharacterDataModified(this, characterDataModifiedEvent);
this.devtools.DOM_ChildNodeCountUpdatedEventHandler += (childNodeCountUpdatedEvent) => DomNodeWrapper.onChildNodeCountUpdated(this, childNodeCountUpdatedEvent);
this.devtools.DOM_ChildNodeInsertedEventHandler += (childNodeInsertedEvent) => DomNodeWrapper.onChildNodeInserted(this, childNodeInsertedEvent);
this.devtools.DOM_ChildNodeRemovedEventHandler += (childNodeRemovedEvent) => DomNodeWrapper.onChildNodeRemoved(this, childNodeRemovedEvent);
this.devtools.DOM_DocumentUpdatedEventHandler += (documentUpdatedEvent) => DomNodeWrapper.onDocumentUpdated(this, documentUpdatedEvent);
this.devtools.DOM_SetChildNodesEventHandler += (setChildNodesEvent) => DomNodeWrapper.onSetChildNodes(this, setChildNodesEvent);
}
public IEnumerator Update()
......@@ -34,10 +47,14 @@ namespace bessw.Unity.WebView.ChromeDevTools
public IEnumerator CreateScreenshot(double width, double height, Action<Texture2D> callback)
{
var screenshotCommand = new captureScreenshot();
screenshotCommand.Clip = new Protocol.Types.Viewport();
screenshotCommand.Clip.Width = width;
screenshotCommand.Clip.Height = height;
var screenshotCommand = new captureScreenshot
{
Clip = new Protocol.Types.Viewport
{
Width = width,
Height = height
}
};
return devtools.SendCommand(screenshotCommand,
(response) => {
......@@ -59,8 +76,10 @@ namespace bessw.Unity.WebView.ChromeDevTools
devtools.screencastFrameEventHandler += (screencastFrameEvent frameEvent) =>
{
// send an ack for this frame
var frameAck = new screencastFrameAck();
frameAck.sessionId = frameEvent.sessionId;
var frameAck = new screencastFrameAck
{
sessionId = frameEvent.sessionId
};
_ = devtools.SendCommandAsync(frameAck);
size = new Vector2Int(frameEvent.metadata.deviceWidth, frameEvent.metadata.deviceHeight);
......@@ -76,10 +95,12 @@ namespace bessw.Unity.WebView.ChromeDevTools
callback(frameTexture);
};
var startScreencast = new startScreencast();
startScreencast.MaxWidth = maxWidth;
startScreencast.maxHeight = maxHeight;
startScreencast.everyNthFrame = 1;
var startScreencast = new startScreencast
{
MaxWidth = maxWidth,
maxHeight = maxHeight,
everyNthFrame = 1
};
return devtools.SendCommand(startScreencast);
}
......@@ -139,6 +160,11 @@ namespace bessw.Unity.WebView.ChromeDevTools
_ = devtools.SendCommandAsync(new cancelDragging());
}
public IEnumerator GetDocument(Action<DomNodeWrapper> callback)
{
return DomNodeWrapper.getDocument(this, callback);
}
/**
* close this tab
*/
......
using bessw.Unity.WebView.ChromeDevTools.Protocol;
using bessw.Unity.WebView.ChromeDevTools.Protocol.DOM;
using bessw.Unity.WebView.ChromeDevTools.Protocol.Page;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
......@@ -25,6 +26,19 @@ namespace bessw.Unity.WebView.ChromeDevTools
public event Action<screencastFrameEvent> screencastFrameEventHandler;
public event Action<screencastVisibilityChangedEvent> screencastVisibilityChangedEventHandler;
#region DOM events
public event Action<attributeModifiedEvent> DOM_AttributeModifiedEventHandler;
public event Action<attributeRemovedEvent> DOM_AttributeRemovedEventHandler;
public event Action<characterDataModifiedEvent> DOM_CharacterDataModifiedEventHandler;
public event Action<childNodeCountUpdatedEvent> DOM_ChildNodeCountUpdatedEventHandler;
public event Action<childNodeInsertedEvent> DOM_ChildNodeInsertedEventHandler;
public event Action<childNodeRemovedEvent> DOM_ChildNodeRemovedEventHandler;
public event Action<documentUpdatedEvent> DOM_DocumentUpdatedEventHandler;
public event Action<setChildNodesEvent> DOM_SetChildNodesEventHandler;
#endregion DOM events
public event Action<DevtoolsEventWrapper> unknownEventHandler;
public DevtoolsProtocolHandler(IDevtoolsConnection devtools)
{
this.devtools = devtools;
......@@ -113,10 +127,44 @@ namespace bessw.Unity.WebView.ChromeDevTools
case "Page.screencastVisibilityChanged":
screencastVisibilityChangedEventHandler?.Invoke( ev.Params.ToObject<screencastVisibilityChangedEvent>(Browser.devtoolsSerializer) );
break;
// switch cases that invoke the event handlers for the DOM events
#region DOM events
case "DOM.attributeModified":
DOM_AttributeModifiedEventHandler?.Invoke( ev.Params.ToObject<attributeModifiedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.attributeRemoved":
DOM_AttributeRemovedEventHandler?.Invoke( ev.Params.ToObject<attributeRemovedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.characterDataModified":
DOM_CharacterDataModifiedEventHandler?.Invoke( ev.Params.ToObject<characterDataModifiedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.childNodeCountUpdated":
DOM_ChildNodeCountUpdatedEventHandler?.Invoke( ev.Params.ToObject<childNodeCountUpdatedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.childNodeInserted":
DOM_ChildNodeInsertedEventHandler?.Invoke( ev.Params.ToObject<childNodeInsertedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.childNodeRemoved":
DOM_ChildNodeRemovedEventHandler?.Invoke( ev.Params.ToObject<childNodeRemovedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.documentUpdated":
DOM_DocumentUpdatedEventHandler?.Invoke( ev.Params.ToObject<documentUpdatedEvent>(Browser.devtoolsSerializer) );
break;
case "DOM.setChildNodes":
DOM_SetChildNodesEventHandler?.Invoke( ev.Params.ToObject<setChildNodesEvent>(Browser.devtoolsSerializer) );
break;
#endregion DOM events
default:
if (unknownEventHandler != null) {
unknownEventHandler.Invoke(ev);
break;
} else {
throw new UnexpectedMessageException($"Event of type '{ev}' is not implemented");
}
}
}
/// <summary>
......
using bessw.Unity.WebView.ChromeDevTools.Protocol.DOM;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
#nullable enable
namespace bessw.Unity.WebView.ChromeDevTools
{
public class DomNodeWrapper
{
/// <summary>
/// Node identifier that is passed into the rest of the DOM messages as the `nodeId`. Backend
/// will only push node with given `id` once. It is aware of all requested nodes and will only
/// fire DOM events for nodes known to the client.
/// </summary>
public int NodeId
{
get => Node.nodeId;
protected set => Node.nodeId = value;
}
/// <summary>
/// The id of the parent node if any.
/// </summary>
public int? ParentId
{
get => Node.parentId;
protected set => Node.parentId = value;
}
/// <summary>
/// The `Node`object returend by the browser
/// </summary>
/// <remarks>May be null if it has not jet been requested from the browser</remarks>
public Node Node { get; protected set; }
/// <summary>
/// Reference to the browser tab that this dom node belongs to
/// </summary>
private BrowserTab tab;
/// <summary>
/// private constructor, create instances with the static method <see cref="getDocument"/>,
/// or by calling instance methods on a <see cref="DomNodeWrapper"/> to get its child nodes.
/// </summary>
/// <param name="tab"></param>
private DomNodeWrapper(BrowserTab tab, Node node)
{
this.tab = tab;
this.Node = node;
}
private static DomNodeWrapper createOrUpdateNode(BrowserTab tab, int nodeId, int? parentId = null, int? backendNodeId = null, Node? node = null)
{
if (tab.domNodes.ContainsKey(nodeId))
{
var domNode = tab.domNodes[nodeId];
if (node != null) domNode.Node = node;
if (parentId != null) domNode.ParentId = parentId;
if (backendNodeId != null) domNode.Node.backendNodeId = (int)backendNodeId;
return domNode;
}
else
{
var domNode = new DomNodeWrapper(tab, node ?? new Node()
{
nodeId = nodeId,
parentId = parentId
});
tab.domNodes[nodeId] = domNode;
return domNode;
}
}
#region Event Handlers
public static void onAttributeModified(BrowserTab tab, attributeModifiedEvent eventData)
{
var domNode = createOrUpdateNode(tab, eventData.nodeId);
if (domNode.Node != null)
{
domNode.Node.attributes[eventData.name] = eventData.value;
} else {
throw new InvalidOperationException("AttributeModified event was fired, the node info has not yet been recieved");
}
}
public static void onAttributeRemoved(BrowserTab tab, attributeRemovedEvent eventData)
{
var domNode = createOrUpdateNode(tab, eventData.nodeId);
if (domNode.Node != null)
{
domNode.Node.attributes.Remove(eventData.name);
} else {
throw new InvalidOperationException("AttributeRemoved event was fired, the node info has not yet been recieved");
}
}
public static void onCharacterDataModified(BrowserTab tab, characterDataModifiedEvent eventData)
{
var domNode = createOrUpdateNode(tab, eventData.nodeId);
if (domNode.Node != null)
{
domNode.Node.nodeValue = eventData.characterData;
} else {
throw new InvalidOperationException("CharacterDataModified event was fired, the node info has not yet been recieved");
}
}
public static void onChildNodeCountUpdated(BrowserTab tab, childNodeCountUpdatedEvent eventData)
{
var domNode = createOrUpdateNode(tab, eventData.nodeId);
if (domNode.Node != null)
{
domNode.Node.childNodeCount = eventData.childNodeCount;
} else {
throw new InvalidOperationException("ChildNodeCountUpdated event was fired, the node info has not yet been recieved");
}
}
public static void onChildNodeInserted(BrowserTab tab, childNodeInsertedEvent eventData)
{
var parentNode = createOrUpdateNode(tab, eventData.parentNodeId);
var childNode = createOrUpdateNode(tab, eventData.node.nodeId, eventData.parentNodeId, eventData.node.backendNodeId, eventData.node);
if (parentNode.Node != null && childNode.Node != null)
{
parentNode.Node.children.Insert(eventData.previousNodeId, childNode.Node);
} else {
throw new InvalidOperationException("ChildNodeInserted event was fired, the node info has not yet been recieved");
}
}
public static void onChildNodeRemoved(BrowserTab tab, childNodeRemovedEvent eventData)
{
if (
tab.domNodes.TryGetValue(eventData.parentNodeId, out var parentNode) &&
tab.domNodes.TryGetValue(eventData.nodeId, out var childNode) &&
parentNode.Node != null &&
childNode.Node != null)
{
parentNode.Node.children.Remove(childNode.Node);
} else {
//throw new InvalidOperationException("ChildNodeRemoved event was fired, the node info has not yet been recieved");
}
}
public static void onDocumentUpdated(BrowserTab tab, documentUpdatedEvent eventData)
{
// clear the domNodes dictionary
tab.domNodes.Clear();
}
public static void onSetChildNodes(BrowserTab tab, setChildNodesEvent eventData)
{
var parentNode = createOrUpdateNode(tab, eventData.parentId);
if (parentNode.Node != null)
{
foreach (var node in eventData.nodes)
{
createOrUpdateNode(tab, node.nodeId, eventData.parentId, node.backendNodeId, node);
}
parentNode.Node.children = new(eventData.nodes);
} else {
throw new InvalidOperationException("SetChildNodes event was fired, the node info has not yet been recieved");
}
}
#endregion Event Handlers
#region Commands
/// <summary>
/// Enables DOM agent for the given page.
/// </summary>
public static IEnumerator enable(BrowserTab tab)
{
var enableCommand = new enable();
return tab.devtools.SendCommand(enableCommand);
}
/// <summary>
/// Disables DOM agent for the given page.
/// </summary>
public static IEnumerator disable(BrowserTab tab)
{
return tab.devtools.SendCommand( new disable() );
}
/// <summary>
/// Get the root node of the document, the root node is returned as an argument to the callback.
/// </summary>
/// <param name="tab"></param>
/// <param name="callback">The callback recieves the root node as argument</param>
/// <returns><see cref="DomNodeWrapper"/></returns>
public static IEnumerator getDocument(BrowserTab tab, Action<DomNodeWrapper> callback)
{
var domCommand = new getDocument();
return tab.devtools.SendCommand(domCommand, (response) =>
{
Node documentRoot = ((getDocumentCommandResponse)response).root;
var domNode = createOrUpdateNode(tab,
documentRoot.nodeId,
documentRoot.parentId,
documentRoot.backendNodeId,
documentRoot
);
callback(domNode);
});
}
/// <summary>
/// Describes the node, does not require domain to be enabled. Does not start tracking any
/// objects, can be used for automation.
/// </summary>
/// <param name="callback">The callback recieves the node as argument</param>
public IEnumerator describeNode(Action<Node> callback, int? depth = null, bool? pierce = null)
{
var describeNodeCommand = new describeNode
{
nodeId = NodeId,
depth = depth,
pierce = pierce
};
return tab.devtools.SendCommand(describeNodeCommand, (response) =>
{
callback(((describeNodeCommandResponse)response).node);
});
}
/// <summary>
/// Focus this node.
/// </summary>
public IEnumerator focus()
{
var focusCommand = new focus
{
nodeId = NodeId
};
return tab.devtools.SendCommand(focusCommand);
}
/// <summary>
/// Gets the nodes Attributes
/// </summary>
public IEnumerator getAttributes(Action<Dictionary<string, string>> callback)
{
var attributesCommand = new getAttributes
{
nodeId = NodeId
};
return tab.devtools.SendCommand(attributesCommand, (response) =>
{
var attibutes = ((getAttributesCommandResponse)response).attributes;
tab.domNodes[NodeId].Node.attributes = attibutes;
callback(attibutes);
});
}
public IEnumerator getBoxModel(Action<BoxModel> callback)
{
var boxModelCommand = new getBoxModel
{
nodeId = NodeId
};
return tab.devtools.SendCommand(boxModelCommand, (response) =>
{
callback(((getBoxModelCommandResponse) response).model);
});
}
public IEnumerator getNodeForLocation(int x, int y, Action<DomNodeWrapper> callback, Action<string>? errorCallback = null)
{
var nodeForLocationCommand = new getNodeForLocation
{
x = x,
y = y
};
return tab.devtools.SendCommand(nodeForLocationCommand, (response) =>
{
var responseNodeId = ((getNodeForLocationCommandResponse) response).nodeId;
if (responseNodeId != null) {
var domNode = createOrUpdateNode(tab, (int)responseNodeId);
callback(domNode);
} else {
errorCallback?.Invoke("nodeId was not provided, is the dom domain enabled?");
}
});
}
/// <summary>
/// Returns node's HTML markup.
/// </summary>
public IEnumerator getOuterHtml(Action<string> callback)
{
var outerHtmlCommand = new getOuterHTML
{
nodeId = NodeId
};
return tab.devtools.SendCommand(outerHtmlCommand, (response) =>
{
callback(((getOuterHTMLCommandResponse) response).outerHTML);
});
}
/// <summary>
/// Moves node into an other container node
/// </summary>
public IEnumerator moveTo(DomNodeWrapper targetNode, DomNodeWrapper? insertBeforeNode = null)
{
var moveToCommand = new moveTo
{
nodeId = NodeId,
targetNodeId = targetNode.NodeId,
insertBeforeNodeId = insertBeforeNode?.NodeId
};
return tab.devtools.SendCommand(moveToCommand, (response) =>
{
// update the new node id of the moved node
tab.domNodes.Remove(NodeId);
NodeId = ((moveToCommandResponse) response).nodeId;
tab.domNodes[NodeId] = this;
});
}
/// <summary>
/// search a child node by querySelector
/// </summary>
public IEnumerator querySelector(string selector, Action<DomNodeWrapper> callback)
{
var querySelectorCommand = new querySelector
{
nodeId = NodeId,
selector = selector
};
return tab.devtools.SendCommand(querySelectorCommand, (response) =>
{
var domNode = createOrUpdateNode(tab, ((querySelectorCommandResponse) response).nodeId);
callback(domNode);
});
}
/// <summary>
/// search all child nodes matching the querySelector
/// </summary>
public IEnumerator querySelectorAll(string selector, Action<DomNodeWrapper[]> callback)
{
var querySelectorAllCommand = new querySelectorAll
{
nodeId = NodeId,
selector = selector
};
return tab.devtools.SendCommand(querySelectorAllCommand, (response) =>
{
var nodeIds = ((querySelectorAllCommandResponse) response).nodeIds;
var domNodes = new DomNodeWrapper[nodeIds.Length];
for (int i = 0; i < nodeIds.Length; i++)
{
domNodes[i] = createOrUpdateNode(tab, nodeIds[i]);
}
callback(domNodes);
});
}
//TODO: implement the other commands of the DOM domain
#endregion Commands
}
}
fileFormatVersion: 2
guid: 9a49fdc400bfe5d46b76c0c1e154940e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
#nullable enable annotations
namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
{
#region commands
......@@ -5,30 +11,35 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Describes node given its id, does not require domain to be enabled. Does not start tracking any
/// objects, can be used for automation.
/// </summary>
[CommandResponseAttribute(typeof(describeNodeCommandResponse))]
public class describeNode : IDevtoolsCommand
[CommandResponse(typeof(describeNodeCommandResponse))]
public class describeNode : IDevtoolsCommandWithResponse
{
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
/// <summary>
/// The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the
/// entire subtree or provide an integer larger than 0.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? depth { get; set; }
/// <summary>
/// Whether or not iframes and shadow roots should be traversed when returning the subtree
/// (default is false).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? pierce { get; set; }
}
......@@ -57,6 +68,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Whether to include whitespaces in the children array of returned Nodes.
/// </summary>
/// <remarks>experimental</remarks>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? includeWhitespace { get; set; }
}
......@@ -68,22 +80,25 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
}
/// <summary>
/// Returns attributes for the specified node.
/// </summary>
[CommandResponseAttribute(typeof(getAttributesCommandResponse))]
public class getAttributes : IDevtoolsCommand
[CommandResponse(typeof(getAttributesCommandResponse))]
public class getAttributes : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to retrieve attibutes for.
......@@ -99,26 +114,30 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// An interleaved array of node attribute names and values.
/// </summary>
public string[] attributes { get; set; }
[JsonConverter(typeof(JsonInterleavedArrayConverter<string, string>))]
public Dictionary<string, string> attributes { get; set; }
}
/// <summary>
/// Returns boxes for the currently selected nodes.
/// </summary>
[CommandResponseAttribute(typeof(getBoxModelCommandResponse))]
public class getBoxModel : IDevtoolsCommand
[CommandResponse(typeof(getBoxModelCommandResponse))]
public class getBoxModel : IDevtoolsCommandWithResponse
{
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
}
......@@ -138,18 +157,20 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Returns the root DOM node (and optionally the subtree) to the caller.
/// Implicitly enables the DOM domain events for the current target.
/// </summary>
[CommandResponseAttribute(typeof(getDocumentCommandResponse))]
public class getDocument : IDevtoolsCommand
[CommandResponse(typeof(getDocumentCommandResponse))]
public class getDocument : IDevtoolsCommandWithResponse
{
/// <summary>
/// The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the
/// entire subtree or provide an integer larger than 0.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? depth { get; set; }
/// <summary>
/// Whether or not iframes and shadow roots should be traversed when returning the subtree
/// (default is false).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? pierce { get; set; }
}
......@@ -169,8 +190,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Returns node id at given location. Depending on whether DOM domain is enabled, nodeId is
/// either returned or not.
/// </summary>
[CommandResponseAttribute(typeof(getNodeForLocationCommandResponse))]
public class getNodeForLocation : IDevtoolsCommand
[CommandResponse(typeof(getNodeForLocationCommandResponse))]
public class getNodeForLocation : IDevtoolsCommandWithResponse
{
/// <summary>
/// X coordinate.
......@@ -183,10 +204,12 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// False to skip to the nearest non-UA shadow root ancestor (default: false).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? includeUserAgentShadowDOM { get; set; }
/// <summary>
/// Whether to ignore pointer-events: none on elements and hit test them.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? ignorePointerEventsNone { get; set; }
}
......@@ -198,14 +221,15 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Resulting node.
/// </summary>
public int? backendNodeId { get; set; }
public int backendNodeId { get; set; }
/// <summary>
/// Frame this node belongs to.
/// </summary>
public string frameId { get; set; }
/// <summary>
/// Id of the node at given coordinates.
/// Id of the node at given coordinates, only when enabled and requested document.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
}
......@@ -213,20 +237,23 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Returns node's HTML markup.
/// </summary>
[CommandResponseAttribute(typeof(getOuterHTMLCommandResponse))]
public class getOuterHTML : IDevtoolsCommand
[CommandResponse(typeof(getOuterHTMLCommandResponse))]
public class getOuterHTML : IDevtoolsCommandWithResponse
{
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
}
......@@ -245,6 +272,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Hides any highlight.
/// </summary>
/// <remarks>see Overlay.hideHighlight</remarks>
public class hideHighlight : IDevtoolsCommand
{
}
......@@ -252,6 +280,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Highlights DOM node.
/// </summary>
/// <remarks>see Overlay.highlightNode</remarks>
public class highlightNode : IDevtoolsCommand
{
}
......@@ -259,6 +288,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// highlights given rectangle.
/// </summary>
/// <remarks>see Overlay.highlightRect</remarks>
public class highlightRect : IDevtoolsCommand
{
}
......@@ -266,8 +296,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Moves node into the new container, places it before the given anchor.
/// </summary>
[CommandResponseAttribute(typeof(moveToCommandResponse))]
public class moveTo : IDevtoolsCommand
[CommandResponse(typeof(moveToCommandResponse))]
public class moveTo : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to move.
......@@ -281,6 +311,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Drop node before this one (if absent, the moved node becomes the last child of
/// `targetNodeId`).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? insertBeforeNodeId { get; set; }
}
......@@ -299,8 +330,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Executes `querySelector` on a given node.
/// </summary>
[CommandResponseAttribute(typeof(querySelectorCommandResponse))]
public class querySelector : IDevtoolsCommand
[CommandResponse(typeof(querySelectorCommandResponse))]
public class querySelector : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to query upon.
......@@ -327,8 +358,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Executes `querySelectorAll` on a given node.
/// </summary>
[CommandResponseAttribute(typeof(querySelectorAllCommandResponse))]
public class querySelectorAll : IDevtoolsCommand
[CommandResponse(typeof(querySelectorAllCommandResponse))]
public class querySelectorAll : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to query upon.
......@@ -393,11 +424,13 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// The maximum depth at which children should be retrieved, defaults to 1. Use -1 for the
/// entire subtree or provide an integer larger than 0.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? depth { get; set; }
/// <summary>
/// Whether or not iframes and shadow roots should be traversed when returning the sub-tree
/// (default is false).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? pierce { get; set; }
}
......@@ -406,8 +439,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// nodes that form the path from the node to the root are also sent to the client as a series of
/// `setChildNodes` notifications.
/// </summary>
[CommandResponseAttribute(typeof(requestNodeCommandResponse))]
public class requestNode : IDevtoolsCommand
[CommandResponse(typeof(requestNodeCommandResponse))]
public class requestNode : IDevtoolsCommandWithResponse
{
/// <summary>
/// JavaScript object id to convert into node.
......@@ -431,8 +464,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Resolves the JavaScript node object for a given NodeId or BackendNodeId.
/// </summary>
[CommandResponseAttribute(typeof(resolveNodeCommandResponse))]
public class resolveNode : IDevtoolsCommand
[CommandResponse(typeof(resolveNodeCommandResponse))]
public class resolveNode : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to resolve.
......@@ -474,19 +507,23 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
/// <summary>
/// The rect to be scrolled into view, relative to the node's border box, in CSS pixels.
/// When omitted, center of the node will be used, similar to Element.scrollIntoView.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Rect? rect { get; set; }
}
......@@ -508,6 +545,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// Attribute name to replace with new attributes derived from text in case text parsed
/// successfully.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? name { get; set; }
}
......@@ -542,22 +580,25 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Identifier of the node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? nodeId { get; set; }
/// <summary>
/// Identifier of the backend node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? backendNodeId { get; set; }
/// <summary>
/// JavaScript object id of the node wrapper.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? objectId { get; set; }
}
/// <summary>
/// Set node name for a node with given id.
/// </summary>
[CommandResponseAttribute(typeof(setNodeNameCommandResponse))]
public class setNodeName : IDevtoolsCommand
[CommandResponse(typeof(setNodeNameCommandResponse))]
public class setNodeName : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to set name for.
......@@ -599,8 +640,8 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Sets node HTML markup, returns new node id.
/// </summary>
[CommandResponseAttribute(typeof(setOuterHTMLCommandResponse))]
public class setOuterHTML : IDevtoolsCommand
[CommandResponse(typeof(setOuterHTMLCommandResponse))]
public class setOuterHTML : IDevtoolsCommandWithResponse
{
/// <summary>
/// Id of the node to set markup for.
......@@ -802,6 +843,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Shape outside coordinates
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public ShapeOutsideInfo? shapeOutside { get; set; }
}
......@@ -850,6 +892,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// The id of the parent node if any.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? parentId { get; set; }
/// <summary>
/// The BackendNodeId for this node.
......@@ -874,79 +917,97 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// Child count for `Container` nodes.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public int? childNodeCount { get; set; }
/// <summary>
/// Child nodes of this node when requested with children.
/// </summary>
public Node[]? children { get; set; }
public List<Node> children { get; set; }
/// <summary>
/// Attributes of the `Element` node in the form of flat array `[name1, value1, name2, value2]`.
/// </summary>
public string[]? attributes { get; set; }
[JsonConverter(typeof(JsonInterleavedArrayConverter<string, string>))]
public Dictionary<string,string> attributes { get; set; }
/// <summary>
/// Document URL that `Document` or `FrameOwner` node points to.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? documentURL { get; set; }
/// <summary>
/// Base URL that `Document` or `FrameOwner` node uses for URL completion.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? baseURL { get; set; }
/// <summary>
/// `DocumentType`'s publicId.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? publicId { get; set; }
/// <summary>
/// `DocumentType`'s systemId.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? systemId { get; set; }
/// <summary>
/// `DocumentType`'s internalSubset.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? internalSubset { get; set; }
/// <summary>
/// `Document`'s XML version in case of XML documents.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? xmlVersion { get; set; }
/// <summary>
/// `Attr`'s name.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? name { get; set; }
/// <summary>
/// `Attr`'s value.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? value { get; set; }
/// <summary>
/// Pseudo element type for this node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public PseudoType? pseudoType { get; set; }
/// <summary>
/// Pseudo element identifier for this node. Only present if there is a
/// valid pseudoType.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? pseudoIdentifier { get; set; }
/// <summary>
/// Shadow root type.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public ShadowRootType? shadowRootType { get; set; }
/// <summary>
/// Frame ID for frame owner elements.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string? frameId { get; set; }
/// <summary>
/// Content document for frame owner elements.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Node? contentDocument { get; set; }
/// <summary>
/// Shadow root list for given element host.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Node[]? shadowRoots { get; set; }
/// <summary>
/// Content document fragment for template elements.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Node? templateContent { get; set; }
/// <summary>
/// Pseudo elements associated with this node.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Node[]? pseudoElements { get; set; }
/// <summary>
/// Deprecated, as the HTML Imports API has been removed (crbug.com/937746).
......@@ -954,16 +1015,21 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// The property is always undefined now.
/// </summary>
/// <remarks>deprecated</remarks>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Node? importedDocument { get; set; }
/// <summary>
/// Distributed nodes for given insertion point.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public BackendNode[]? distributedNodes { get; set; }
/// <summary>
/// Whether the node is SVG.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? isSVG { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public CompatibilityMode? compatibilityMode { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public BackendNode? assignedSlot { get; set; }
}
......@@ -985,6 +1051,17 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
firstLine, firstLetter, before, after, marker, backdrop, selection, targetText, spellingError, grammarError, highlight, firstLineInherited, scrollbar, scrollbarThumb, scrollbarButton, scrollbarTrack, scrollbarTrackPiece, scrollbarCorner, resizer, inputListButton, viewTransition, viewTransitionGroup, viewTransitionImagePair, viewTransitionOld, viewTransitionNew
}
/// <summary>
/// An array of quad vertices, x immediately followed by y for each point, points clock-wise.
/// </summary>
public class Quad
{
/// <summary>
/// Quad vertices
/// </summary>
public double[] quad { get; set; }
}
/// <summary>
/// Rectangle.
/// </summary>
......@@ -1028,6 +1105,7 @@ namespace bessw.Unity.WebView.ChromeDevTools.Protocol.DOM
/// <summary>
/// The alpha component, in the [0-1] range (default: 1).
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public double? a { get; set; }
}
......
fileFormatVersion: 2
guid: f4e9b65319713e3478ca1d350419f8c4
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
......@@ -5,6 +5,7 @@ using Newtonsoft.Json.Serialization;
using UnityEngine.EventSystems;
using static UnityEngine.EventSystems.PointerEventData;
#nullable enable annotations
namespace bessw.Unity.WebView.ChromeDevTools.Protocol.Input
{
#region dispatchMouseEvent
......
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace bessw.Unity.WebView.ChromeDevTools.Protocol
{
/// <summary>
/// Converts a interleaved JSON array like `[key1, value1, key2, value2, ...]` to a C# dictionary.
/// </summary>
/// <typeparam name="keyT"></typeparam>
/// <typeparam name="valueT"></typeparam>
public class JsonInterleavedArrayConverter<keyT, valueT> : JsonConverter<Dictionary<keyT,valueT>>
{
public override void WriteJson(JsonWriter writer, Dictionary<keyT,valueT> value, JsonSerializer serializer)
{
JArray obj = new JArray();
foreach (var pair in value)
{
obj.Add(pair.Key);
obj.Add(pair.Value);
}
obj.WriteTo(writer);
}
public override Dictionary<keyT,valueT> ReadJson(JsonReader reader, Type objectType, Dictionary<keyT,valueT> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
JArray obj = JArray.Load(reader);
Dictionary<keyT,valueT> result = new Dictionary<keyT,valueT>();
for (int i = 0; i < obj.Count; i+=2)
{
result.Add(obj[i].Value<keyT>(), obj[i+1].Value<valueT>());
}
return result;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 31c472a5e6adcf247b4e0d9835835e75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
......@@ -39,7 +39,7 @@ namespace bessw.Unity.WebView
#endregion json serializer
private Browser browser;
private BrowserTab tab;
protected BrowserTab tab;
private RawImage rawImage;
private RectTransform rectTransform;
......@@ -87,6 +87,32 @@ namespace bessw.Unity.WebView
});
}
private IEnumerator getDropzoneState ()
{
Debug.LogWarning($"dropzone pre");
DomNodeWrapper doc = null;
yield return DomNodeWrapper.getDocument(tab, (document) => {
Debug.LogWarning($"dropzone 1: '{document}'");
doc = document;
StartCoroutine(document.querySelectorAll("[dropzone='copy']", (dropzones) => {
foreach (var dropzone in dropzones)
{
Debug.LogWarning($"dropzone 2: Node is Null?: '{dropzone.Node == null}'");
StartCoroutine(dropzone.getAttributes((attributes) =>
{
Debug.LogWarning($"dropzone 3 getAttributes: '{string.Join(", ", attributes.Values)}'");
}));
}
}));
});
Debug.LogWarning($"dropzone post: '{doc}'");
// alternative way to get the dropzone state
}
// Update is called once per frame
private void Update()
{
......@@ -132,12 +158,13 @@ namespace bessw.Unity.WebView
}
}
// TODO: OnDragMove -> PointerMove
public void OnDrop(PointerEventData eventData)
{
Debug.LogWarning($"OnDrop: {eventData.position}");
createDragEvent(DragEventType.drop, eventData);
// TODO: remove debug code
StartCoroutine(getDropzoneState());
}
public void OnPointerExit(PointerEventData eventData)
......
......@@ -34,11 +34,11 @@ RectTransform:
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -444.39624, y: -175.33963}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
m_Pivot: {x: 0, y: 0}
--- !u!222 &5559415116192402672
CanvasRenderer:
m_ObjectHideFlags: 0
......@@ -87,3 +87,4 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
headlessBrowser: 1
targetUrl: https://google.de
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment