diff --git a/Runtime/ChromeDevtools/BrowserTab.cs b/Runtime/ChromeDevtools/BrowserTab.cs
index 0f4e23f92575d37e712e05287aee5b0c0171114f..4daa6b1c4c86cec896be7341c8b0a55304991511 100644
--- a/Runtime/ChromeDevtools/BrowserTab.cs
+++ b/Runtime/ChromeDevtools/BrowserTab.cs
@@ -30,14 +30,14 @@ namespace bessw.Unity.WebView.ChromeDevTools
             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);
+            this.devtools.onDomAttributeModified += (attributeModifiedEvent) => DomNodeWrapper.AttributeModifiedEventHandler(this, attributeModifiedEvent);
+            this.devtools.onDomAttributeRemoved += (attributeRemovedEvent) => DomNodeWrapper.AttributeRemovedEventHandler(this, attributeRemovedEvent);
+            this.devtools.onDomCharacterDataModified += (characterDataModifiedEvent) => DomNodeWrapper.CharacterDataModifiedEventHandler(this, characterDataModifiedEvent);
+            this.devtools.onDomChildNodeCountUpdated += (childNodeCountUpdatedEvent) => DomNodeWrapper.ChildNodeCountUpdatedEventHandler(this, childNodeCountUpdatedEvent);
+            this.devtools.onDomChildNodeInserted += (childNodeInsertedEvent) => DomNodeWrapper.ChildNodeInsertedEventHandler(this, childNodeInsertedEvent);
+            this.devtools.onDomChildNodeRemoved += (childNodeRemovedEvent) => DomNodeWrapper.ChildNodeRemovedEventHandler(this, childNodeRemovedEvent);
+            this.devtools.onDomDocumentUpdated += (documentUpdatedEvent) => DomNodeWrapper.DocumentUpdatedEventHandler(this, documentUpdatedEvent);
+            this.devtools.onDomSetChildNodes += (setChildNodesEvent) => DomNodeWrapper.SetChildNodesEventHandler(this, setChildNodesEvent);
         }
 
         public IEnumerator Update()
@@ -73,7 +73,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
         {
             // register screencast frame event handler
             // TODO: deregister on stop stream
-            devtools.screencastFrameEventHandler += (screencastFrameEvent frameEvent) =>
+            devtools.onScreencastFrame += (screencastFrameEvent frameEvent) =>
             {
                 // send an ack for this frame
                 var frameAck = new screencastFrameAck
diff --git a/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs b/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
index 737ffe341b6bcb820180248fe7d930099315dead..97e6146f4591b533080071a29c28782647b4c4b6 100644
--- a/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
+++ b/Runtime/ChromeDevtools/DevtoolsProtocolHandler.cs
@@ -23,18 +23,18 @@ namespace bessw.Unity.WebView.ChromeDevTools
         private ConcurrentDictionary<long, ResponseTypeAndCallback> commandResponseDict = new ConcurrentDictionary<long, ResponseTypeAndCallback>();
 
         // devtools events
-        public event Action<screencastFrameEvent> screencastFrameEventHandler;
-        public event Action<screencastVisibilityChangedEvent> screencastVisibilityChangedEventHandler;
+        public event Action<screencastFrameEvent> onScreencastFrame;
+        public event Action<screencastVisibilityChangedEvent> onScreencastVisibilityChanged;
 
         #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;
+        public event Action<attributeModifiedEvent> onDomAttributeModified;
+        public event Action<attributeRemovedEvent> onDomAttributeRemoved;
+        public event Action<characterDataModifiedEvent> onDomCharacterDataModified;
+        public event Action<childNodeCountUpdatedEvent> onDomChildNodeCountUpdated;
+        public event Action<childNodeInsertedEvent> onDomChildNodeInserted;
+        public event Action<childNodeRemovedEvent> onDomChildNodeRemoved;
+        public event Action<documentUpdatedEvent> onDomDocumentUpdated;
+        public event Action<setChildNodesEvent> onDomSetChildNodes;
         #endregion DOM events
 
         public event Action<DevtoolsEventWrapper> unknownEventHandler;
@@ -122,37 +122,37 @@ namespace bessw.Unity.WebView.ChromeDevTools
             switch (ev.Method)
             {
                 case "Page.screencastFrame":
-                    screencastFrameEventHandler?.Invoke( ev.Params.ToObject<screencastFrameEvent>(Browser.devtoolsSerializer) );
+                    onScreencastFrame?.Invoke( ev.Params.ToObject<screencastFrameEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "Page.screencastVisibilityChanged":
-                    screencastVisibilityChangedEventHandler?.Invoke( ev.Params.ToObject<screencastVisibilityChangedEvent>(Browser.devtoolsSerializer) );
+                    onScreencastVisibilityChanged?.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) );
+                    onDomAttributeModified?.Invoke( ev.Params.ToObject<attributeModifiedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.attributeRemoved":
-                    DOM_AttributeRemovedEventHandler?.Invoke( ev.Params.ToObject<attributeRemovedEvent>(Browser.devtoolsSerializer) );
+                    onDomAttributeRemoved?.Invoke( ev.Params.ToObject<attributeRemovedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.characterDataModified":
-                    DOM_CharacterDataModifiedEventHandler?.Invoke( ev.Params.ToObject<characterDataModifiedEvent>(Browser.devtoolsSerializer) );
+                    onDomCharacterDataModified?.Invoke( ev.Params.ToObject<characterDataModifiedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.childNodeCountUpdated":
-                    DOM_ChildNodeCountUpdatedEventHandler?.Invoke( ev.Params.ToObject<childNodeCountUpdatedEvent>(Browser.devtoolsSerializer) );
+                    onDomChildNodeCountUpdated?.Invoke( ev.Params.ToObject<childNodeCountUpdatedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.childNodeInserted":
-                    DOM_ChildNodeInsertedEventHandler?.Invoke( ev.Params.ToObject<childNodeInsertedEvent>(Browser.devtoolsSerializer) );
+                    onDomChildNodeInserted?.Invoke( ev.Params.ToObject<childNodeInsertedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.childNodeRemoved":
-                    DOM_ChildNodeRemovedEventHandler?.Invoke( ev.Params.ToObject<childNodeRemovedEvent>(Browser.devtoolsSerializer) );
+                    onDomChildNodeRemoved?.Invoke( ev.Params.ToObject<childNodeRemovedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.documentUpdated":
-                    DOM_DocumentUpdatedEventHandler?.Invoke( ev.Params.ToObject<documentUpdatedEvent>(Browser.devtoolsSerializer) );
+                    onDomDocumentUpdated?.Invoke( ev.Params.ToObject<documentUpdatedEvent>(Browser.devtoolsSerializer) );
                     break;
                 case "DOM.setChildNodes":
-                    DOM_SetChildNodesEventHandler?.Invoke( ev.Params.ToObject<setChildNodesEvent>(Browser.devtoolsSerializer) );
+                    onDomSetChildNodes?.Invoke( ev.Params.ToObject<setChildNodesEvent>(Browser.devtoolsSerializer) );
                     break;
                 #endregion DOM events
 
diff --git a/Runtime/ChromeDevtools/DomNodeWrapper.cs b/Runtime/ChromeDevtools/DomNodeWrapper.cs
index c59666ea08cb811f13befbc3de192eabed42b2ba..4d6c163fa802dff2a40462c734cfb97967264437 100644
--- a/Runtime/ChromeDevtools/DomNodeWrapper.cs
+++ b/Runtime/ChromeDevtools/DomNodeWrapper.cs
@@ -34,6 +34,14 @@ namespace bessw.Unity.WebView.ChromeDevTools
         /// <remarks>May be null if it has not jet been requested from the browser</remarks>
         public Node Node { get; protected set; }
 
+        public event Action<attributeModifiedEvent>? OnAttributeModified;
+        public event Action<attributeRemovedEvent>? OnDomAttributeRemoved;
+        public event Action<characterDataModifiedEvent>? OnDomCharacterDataModified;
+        public event Action<childNodeCountUpdatedEvent>? OnDomChildNodeCountUpdated;
+        public event Action<childNodeInsertedEvent>? OnDomChildNodeInserted;
+        public event Action<childNodeRemovedEvent>? OnDomChildNodeRemoved;
+        public event Action<setChildNodesEvent>? OnDomSetChildNodes;
+
 
         /// <summary>
         /// Reference to the browser tab that this dom node belongs to
@@ -75,63 +83,68 @@ namespace bessw.Unity.WebView.ChromeDevTools
 
         #region Event Handlers
 
-        public static void onAttributeModified(BrowserTab tab, attributeModifiedEvent eventData)
+        public static void AttributeModifiedEventHandler(BrowserTab tab, attributeModifiedEvent eventData)
         {
             var domNode = createOrUpdateNode(tab, eventData.nodeId);
             if (domNode.Node != null)
             {
                 domNode.Node.attributes[eventData.name] = eventData.value;
+                domNode.OnAttributeModified?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("AttributeModified event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onAttributeRemoved(BrowserTab tab, attributeRemovedEvent eventData)
+        public static void AttributeRemovedEventHandler(BrowserTab tab, attributeRemovedEvent eventData)
         {
             var domNode = createOrUpdateNode(tab, eventData.nodeId);
             if (domNode.Node != null)
             {
                 domNode.Node.attributes.Remove(eventData.name);
+                domNode.OnDomAttributeRemoved?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("AttributeRemoved event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onCharacterDataModified(BrowserTab tab, characterDataModifiedEvent eventData)
+        public static void CharacterDataModifiedEventHandler(BrowserTab tab, characterDataModifiedEvent eventData)
         {
             var domNode = createOrUpdateNode(tab, eventData.nodeId);
             if (domNode.Node != null)
             {
                 domNode.Node.nodeValue = eventData.characterData;
+                domNode.OnDomCharacterDataModified?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("CharacterDataModified event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onChildNodeCountUpdated(BrowserTab tab, childNodeCountUpdatedEvent eventData)
+        public static void ChildNodeCountUpdatedEventHandler(BrowserTab tab, childNodeCountUpdatedEvent eventData)
         {
             var domNode = createOrUpdateNode(tab, eventData.nodeId);
             if (domNode.Node != null)
             {
                 domNode.Node.childNodeCount = eventData.childNodeCount;
+                domNode.OnDomChildNodeCountUpdated?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("ChildNodeCountUpdated event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onChildNodeInserted(BrowserTab tab, childNodeInsertedEvent eventData)
+        public static void ChildNodeInsertedEventHandler(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);
+                parentNode.OnDomChildNodeInserted?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("ChildNodeInserted event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onChildNodeRemoved(BrowserTab tab, childNodeRemovedEvent eventData)
+        public static void ChildNodeRemovedEventHandler(BrowserTab tab, childNodeRemovedEvent eventData)
         {
             if (
                 tab.domNodes.TryGetValue(eventData.parentNodeId, out var parentNode) &&
@@ -140,18 +153,19 @@ namespace bessw.Unity.WebView.ChromeDevTools
                 childNode.Node != null)
             {
                 parentNode.Node.children.Remove(childNode.Node);
+                parentNode.OnDomChildNodeRemoved?.Invoke(eventData);
             } else {
                 //throw new InvalidOperationException("ChildNodeRemoved event was fired, the node info has not yet been recieved");
             }
         }
 
-        public static void onDocumentUpdated(BrowserTab tab, documentUpdatedEvent eventData)
+        public static void DocumentUpdatedEventHandler(BrowserTab tab, documentUpdatedEvent eventData)
         {
             // clear the domNodes dictionary
             tab.domNodes.Clear();
         }
 
-        public static void onSetChildNodes(BrowserTab tab, setChildNodesEvent eventData)
+        public static void SetChildNodesEventHandler(BrowserTab tab, setChildNodesEvent eventData)
         {
             var parentNode = createOrUpdateNode(tab, eventData.parentId);
             if (parentNode.Node != null)
@@ -161,6 +175,7 @@ namespace bessw.Unity.WebView.ChromeDevTools
                     createOrUpdateNode(tab, node.nodeId, eventData.parentId, node.backendNodeId, node);
                 }
                 parentNode.Node.children = new(eventData.nodes);
+                parentNode.OnDomSetChildNodes?.Invoke(eventData);
             } else {
                 throw new InvalidOperationException("SetChildNodes event was fired, the node info has not yet been recieved");
             }
@@ -218,7 +233,137 @@ namespace bessw.Unity.WebView.ChromeDevTools
             return createOrUpdateNode(tab, commandResponse.nodeId);
         }
 
+        public static async Task<DomNodeWrapper> getNodeForLocationAsync(BrowserTab tab, int x, int y, bool? includeUserAgentShadowDOM = null, bool? ignorePointerEventsNone = null)
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<getNodeForLocation, getNodeForLocationCommandResponse>(new getNodeForLocation
+            {
+                x = x,
+                y = y,
+                includeUserAgentShadowDOM = includeUserAgentShadowDOM,
+                ignorePointerEventsNone = ignorePointerEventsNone
+            });
+            if (commandResponse.nodeId == null) throw new InvalidOperationException("nodeId was not provided, is the dom domain enabled?");
+
+            return createOrUpdateNode(tab, (int)commandResponse.nodeId, backendNodeId: commandResponse.backendNodeId);
+        }
+
+        /// <summary>
+        /// Enables DOM agent for the given page.
+        /// </summary>
+        /// <param name="tab"></param>
+        /// <param name="includeWhitespace"></param>
+        /// <returns></returns>
+        public static Task enableAsync(BrowserTab tab, string? includeWhitespace = null)
+        {
+            return tab.devtools.SendCommandAsync(new enable
+            {
+                includeWhitespace = includeWhitespace
+            });
+        }
+
+        /// <summary>
+        /// Disables DOM agent for the given page.
+        /// </summary>
+        /// <param name="tab"></param>
+        /// <returns></returns>
+        public static Task disableAsync(BrowserTab tab)
+        {
+            return tab.devtools.SendCommandAsync(new disable());
+        }
+
+        /// <summary>
+        /// Focus the given node.
+        /// </summary>
+        public async Task focusAsync()
+        {
+            await tab.devtools.SendCommandAsync(new focus
+            {
+                nodeId = NodeId
+            });
+        }
+
+        /// <summary>
+        /// Returns attributes for the specified node.
+        /// </summary>
+        /// <returns></returns>
+        public async Task<Dictionary<string, string>> getAttributesAsync()
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<getAttributes, getAttributesCommandResponse>(new getAttributes
+            {
+                nodeId = NodeId
+            });
+            Node.attributes = commandResponse.attributes;
+            return commandResponse.attributes;
+        }
+
+        /// <summary>
+        /// Returns boxes for the currently selected nodes.
+        /// </summary>
+        /// <returns></returns>
+        public async Task<BoxModel> getBoxModelAsync()
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<getBoxModel, getBoxModelCommandResponse>(new getBoxModel
+            {
+                nodeId = NodeId
+            });
+            return commandResponse.model;
+        }
+
+        /// <summary>
+        /// Returns node's HTML markup.
+        /// </summary>
+        /// <returns></returns>
+        public async Task<string> getOuterHtmlAsync()
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<getOuterHTML, getOuterHTMLCommandResponse>(new getOuterHTML
+            {
+                nodeId = NodeId
+            });
+            return commandResponse.outerHTML;
+        }
+
+        /// <summary>
+        /// Moves node into an other container node, places it before the given node (if absent, the moved node becomes the last child of `targetNode`).
+        /// </summary>
+        /// <param name="targetNode"></param>
+        /// <param name="insertBeforeNode"></param>
+        /// <returns></returns>
+        /// <remarks>TODO: registered event handlers might need to be registered to the new node id again</remarks>
+        public async Task moveToAsync(DomNodeWrapper targetNode, DomNodeWrapper? insertBeforeNode = null)
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<moveTo, moveToCommandResponse>(new moveTo
+            {
+                nodeId = NodeId,
+                targetNodeId = targetNode.NodeId,
+                insertBeforeNodeId = insertBeforeNode?.NodeId
+            });
+            // update the new node id of the moved node
+            // TODO: registered event handlers might need to be registered again to the new node id
+            tab.domNodes.Remove(NodeId);
+            NodeId = commandResponse.nodeId;
+            tab.domNodes[NodeId] = this;
+        }
+
+        /// <summary>
+        /// Search a child node by querySelector.
+        /// </summary>
+        /// <param name="selector"></param>
+        /// <returns></returns>
+        public async Task<DomNodeWrapper> querySelectorAsync(string selector)
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<querySelector, querySelectorCommandResponse>(new querySelector
+            {
+                nodeId = NodeId,
+                selector = selector
+            });
+            return createOrUpdateNode(tab, commandResponse.nodeId);
+        }
 
+        /// <summary>
+        /// Search all child nodes matching the querySelector.
+        /// </summary>
+        /// <param name="selector"></param>
+        /// <returns></returns>
         public async Task<DomNodeWrapper[]> querySelectorAllAsync(string selector)
         {
             var commandResponse = await tab.devtools.SendCommandAsync<querySelectorAll, querySelectorAllCommandResponse>(new querySelectorAll
@@ -235,15 +380,134 @@ namespace bessw.Unity.WebView.ChromeDevTools
             return domNodes;
         }
 
+        /// <summary>
+        /// Removes attribute with given name from the element.
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        public async Task removeAttributeAsync(string name)
+        {
+            await tab.devtools.SendCommandAsync(new removeAttribute
+            {
+                nodeId = NodeId,
+                name = name
+            });
+        }
 
-        public async Task<Dictionary<string, string>> getAttributesAsync()
+        /// <summary>
+        /// Requests that children of the node with given id are returned to the caller in form of
+        /// `setChildNodes` events where not only immediate children are retrieved, but all children down
+        /// to the specified depth.
+        /// </summary>
+        public async Task requestChildNodesAsync(int? depth = null, bool? pierce = null)
         {
-            var commandResponse = await tab.devtools.SendCommandAsync<getAttributes, getAttributesCommandResponse>(new getAttributes
+            await tab.devtools.SendCommandAsync(new requestChildNodes
+            {
+                nodeId = NodeId,
+                depth = depth,
+                pierce = pierce
+            });
+        }
+
+        /// <summary>
+        /// Scrolls the node into view if not already visible.
+        /// </summary>
+        /// <returns></returns>
+        public async Task scrollIntoViewIfNeededAsync()
+        {
+            await tab.devtools.SendCommandAsync(new scrollIntoViewIfNeeded
             {
                 nodeId = NodeId
             });
-            Node.attributes = commandResponse.attributes;
-            return commandResponse.attributes;
+        }
+
+        /// <summary>
+        /// Sets attribute for the node. This method is useful when user edits some existing
+        /// attribute value and types in several attribute name/value pairs.
+        /// </summary>
+        public async Task setAttributesAsText(string text, string? name = null)
+        {
+            await tab.devtools.SendCommandAsync(new setAttributesAsText
+            {
+                nodeId = NodeId,
+                text = text,
+                name = name
+            });
+        }
+
+        /// <summary>
+        /// Sets attribute for this element.
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public async Task setAttributeValue(string name, string value)
+        {
+            await tab.devtools.SendCommandAsync(new setAttributeValue
+            {
+                nodeId = NodeId,
+                name = name,
+                value = value
+            });
+        }
+
+        /// <summary>
+        /// Sets files for the given file input element.
+        /// </summary>
+        public Task setFileInputFilesAsync(string[] files)
+        {
+            return tab.devtools.SendCommandAsync(new setFileInputFiles
+            {
+                nodeId = NodeId,
+                files = files
+            });
+        }
+
+        /// <summary>
+        /// Sets the nodes name.
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        /// <remarks>updates the nodeId</remarks>
+        public async Task setNodeNameAsync(string name)
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<setNodeName,setNodeNameCommandResponse>(new setNodeName
+            {
+                nodeId = NodeId,
+                name = name
+            });
+            // update the new node id of the renamed node
+            // TODO: registered event handlers might need to be registered again to the new node id
+            tab.domNodes.Remove(NodeId);
+            NodeId = commandResponse.nodeId;
+            tab.domNodes[NodeId] = this;
+        }
+
+        public Task setNodeValueAsync(string value)
+        {
+            return tab.devtools.SendCommandAsync(new setNodeValue
+            {
+                nodeId = NodeId,
+                value = value
+            });
+        }
+
+        /// <summary>
+        /// Sets node HTML markup, changes the node id.
+        /// </summary>
+        public async Task setOuterHtmlAsync(string outerHtml)
+        {
+            var commandResponse = await tab.devtools.SendCommandAsync<setOuterHTML,setOuterHTMLCommandResponse>(new setOuterHTML
+            {
+                nodeId = NodeId,
+                outerHTML = outerHtml
+            });
+
+            // update the new node id of the renamed node
+            // TODO: registered event handlers might need to be registered again to the new node id
+            tab.domNodes.Remove(NodeId);
+            NodeId = commandResponse.nodeId;
+            tab.domNodes[NodeId] = this;
         }
 
         #endregion Async Commands
diff --git a/Runtime/WebViewComponent.cs b/Runtime/WebViewComponent.cs
index 7865670bf11799fdb46fbb44b9d4edcf026350d3..c6ff868276bd3b41f8d59a12ee1722821fc6b0ff 100644
--- a/Runtime/WebViewComponent.cs
+++ b/Runtime/WebViewComponent.cs
@@ -1,12 +1,15 @@
 using bessw.Unity.WebView.ChromeDevTools;
+using bessw.Unity.WebView.ChromeDevTools.Protocol.DOM;
 using bessw.Unity.WebView.ChromeDevTools.Protocol.Input;
 using MoreLinq;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using UnityEngine;
+using UnityEngine.Events;
 using UnityEngine.EventSystems;
 using UnityEngine.UI;
 
@@ -43,6 +46,16 @@ namespace bessw.Unity.WebView
         private Browser browser;
         public BrowserTab tab;
 
+        /// <summary>
+        /// Fired when `Document` has been totally updated. Node ids are no longer valid.
+        /// Other DOM events are fired directly on their `DomNodeWrapper` object.
+        /// </summary>
+        public event Action<documentUpdatedEvent> onDomDocumentUpdated
+        {
+            add => tab.devtools.onDomDocumentUpdated += value;
+            remove => tab.devtools.onDomDocumentUpdated -= value;
+        }
+
         private RawImage rawImage;
         private RectTransform rectTransform;