Addition of chromedp-proxy, fixing SendKeys, more
- Adding chromedp-proxy command to help record chrome sessions - Changing the headless writeup to point to the knq chrome-headless docker image - Fixing issues with SendKeys action (should correctly work for all well known keys) - Updated to latest Chrome protocol.json and re-generated API - Other minor fixes
This commit is contained in:
parent
7421a99dcc
commit
4145d8367d
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
out.txt
|
||||
out*.txt
|
||||
old*.txt
|
||||
cdp-*.log
|
||||
cdp-*.txt
|
||||
|
||||
# binaries
|
||||
/chromedp-gen
|
||||
|
|
|
@ -269,6 +269,8 @@ const (
|
|||
AXGlobalStatesHidden AXGlobalStates = "hidden"
|
||||
AXGlobalStatesHiddenRoot AXGlobalStates = "hiddenRoot"
|
||||
AXGlobalStatesInvalid AXGlobalStates = "invalid"
|
||||
AXGlobalStatesKeyshortcuts AXGlobalStates = "keyshortcuts"
|
||||
AXGlobalStatesRoledescription AXGlobalStates = "roledescription"
|
||||
)
|
||||
|
||||
// MarshalEasyJSON satisfies easyjson.Marshaler.
|
||||
|
@ -292,6 +294,10 @@ func (t *AXGlobalStates) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|||
*t = AXGlobalStatesHiddenRoot
|
||||
case AXGlobalStatesInvalid:
|
||||
*t = AXGlobalStatesInvalid
|
||||
case AXGlobalStatesKeyshortcuts:
|
||||
*t = AXGlobalStatesKeyshortcuts
|
||||
case AXGlobalStatesRoledescription:
|
||||
*t = AXGlobalStatesRoledescription
|
||||
|
||||
default:
|
||||
in.AddError(errors.New("unknown AXGlobalStates value"))
|
||||
|
@ -435,6 +441,7 @@ func (t AXWidgetStates) String() string {
|
|||
const (
|
||||
AXWidgetStatesChecked AXWidgetStates = "checked"
|
||||
AXWidgetStatesExpanded AXWidgetStates = "expanded"
|
||||
AXWidgetStatesModal AXWidgetStates = "modal"
|
||||
AXWidgetStatesPressed AXWidgetStates = "pressed"
|
||||
AXWidgetStatesSelected AXWidgetStates = "selected"
|
||||
)
|
||||
|
@ -456,6 +463,8 @@ func (t *AXWidgetStates) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|||
*t = AXWidgetStatesChecked
|
||||
case AXWidgetStatesExpanded:
|
||||
*t = AXWidgetStatesExpanded
|
||||
case AXWidgetStatesModal:
|
||||
*t = AXWidgetStatesModal
|
||||
case AXWidgetStatesPressed:
|
||||
*t = AXWidgetStatesPressed
|
||||
case AXWidgetStatesSelected:
|
||||
|
@ -483,9 +492,11 @@ func (t AXRelationshipAttributes) String() string {
|
|||
// AXRelationshipAttributes values.
|
||||
const (
|
||||
AXRelationshipAttributesActivedescendant AXRelationshipAttributes = "activedescendant"
|
||||
AXRelationshipAttributesFlowto AXRelationshipAttributes = "flowto"
|
||||
AXRelationshipAttributesControls AXRelationshipAttributes = "controls"
|
||||
AXRelationshipAttributesDescribedby AXRelationshipAttributes = "describedby"
|
||||
AXRelationshipAttributesDetails AXRelationshipAttributes = "details"
|
||||
AXRelationshipAttributesErrormessage AXRelationshipAttributes = "errormessage"
|
||||
AXRelationshipAttributesFlowto AXRelationshipAttributes = "flowto"
|
||||
AXRelationshipAttributesLabelledby AXRelationshipAttributes = "labelledby"
|
||||
AXRelationshipAttributesOwns AXRelationshipAttributes = "owns"
|
||||
)
|
||||
|
@ -505,12 +516,16 @@ func (t *AXRelationshipAttributes) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|||
switch AXRelationshipAttributes(in.String()) {
|
||||
case AXRelationshipAttributesActivedescendant:
|
||||
*t = AXRelationshipAttributesActivedescendant
|
||||
case AXRelationshipAttributesFlowto:
|
||||
*t = AXRelationshipAttributesFlowto
|
||||
case AXRelationshipAttributesControls:
|
||||
*t = AXRelationshipAttributesControls
|
||||
case AXRelationshipAttributesDescribedby:
|
||||
*t = AXRelationshipAttributesDescribedby
|
||||
case AXRelationshipAttributesDetails:
|
||||
*t = AXRelationshipAttributesDetails
|
||||
case AXRelationshipAttributesErrormessage:
|
||||
*t = AXRelationshipAttributesErrormessage
|
||||
case AXRelationshipAttributesFlowto:
|
||||
*t = AXRelationshipAttributesFlowto
|
||||
case AXRelationshipAttributesLabelledby:
|
||||
*t = AXRelationshipAttributesLabelledby
|
||||
case AXRelationshipAttributesOwns:
|
||||
|
|
40
cdp/cdp.go
40
cdp/cdp.go
|
@ -214,6 +214,7 @@ const (
|
|||
CommandDOMEnable MethodType = "DOM.enable"
|
||||
CommandDOMDisable MethodType = "DOM.disable"
|
||||
CommandDOMGetDocument MethodType = "DOM.getDocument"
|
||||
CommandDOMGetFlattenedDocument MethodType = "DOM.getFlattenedDocument"
|
||||
CommandDOMCollectClassNamesFromSubtree MethodType = "DOM.collectClassNamesFromSubtree"
|
||||
CommandDOMRequestChildNodes MethodType = "DOM.requestChildNodes"
|
||||
CommandDOMQuerySelector MethodType = "DOM.querySelector"
|
||||
|
@ -794,6 +795,8 @@ func (t *MethodType) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
|||
*t = CommandDOMDisable
|
||||
case CommandDOMGetDocument:
|
||||
*t = CommandDOMGetDocument
|
||||
case CommandDOMGetFlattenedDocument:
|
||||
*t = CommandDOMGetFlattenedDocument
|
||||
case CommandDOMCollectClassNamesFromSubtree:
|
||||
*t = CommandDOMCollectClassNamesFromSubtree
|
||||
case CommandDOMRequestChildNodes:
|
||||
|
@ -1651,6 +1654,7 @@ func (t *ShadowRootType) UnmarshalJSON(buf []byte) error {
|
|||
// represent the actual DOM nodes. DOMNode is a base node mirror type.
|
||||
type Node struct {
|
||||
NodeID NodeID `json:"nodeId,omitempty"` // 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.
|
||||
ParentID NodeID `json:"parentId,omitempty"` // The id of the parent node if any.
|
||||
BackendNodeID BackendNodeID `json:"backendNodeId,omitempty"` // The BackendNodeId for this node.
|
||||
NodeType NodeType `json:"nodeType,omitempty"` // Node's nodeType.
|
||||
NodeName string `json:"nodeName,omitempty"` // Node's nodeName.
|
||||
|
@ -1698,7 +1702,7 @@ func (n *Node) AttributeValue(name string) string {
|
|||
}
|
||||
|
||||
// xpath builds the xpath string.
|
||||
func (n *Node) xpath(stopWhenID bool) string {
|
||||
func (n *Node) xpath(stopAtDocument, stopAtID bool) string {
|
||||
p := ""
|
||||
pos := ""
|
||||
id := n.AttributeValue("id")
|
||||
|
@ -1706,7 +1710,10 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
case n.Parent == nil:
|
||||
return n.LocalName
|
||||
|
||||
case stopWhenID && id != "":
|
||||
case stopAtDocument && n.NodeType == NodeTypeDocument:
|
||||
return ""
|
||||
|
||||
case stopAtID && id != "":
|
||||
p = "/"
|
||||
pos = `[@id='` + id + `']`
|
||||
|
||||
|
@ -1723,7 +1730,7 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
p = n.Parent.xpath(stopWhenID)
|
||||
p = n.Parent.xpath(stopAtDocument, stopAtID)
|
||||
if found {
|
||||
pos = "[" + strconv.Itoa(i) + "]"
|
||||
}
|
||||
|
@ -1732,15 +1739,28 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
return p + "/" + n.LocalName + pos
|
||||
}
|
||||
|
||||
// XPathByID returns the XPath tree for the node, stopping at the first parent
|
||||
// with an id attribute.
|
||||
func (n *Node) XPathByID() string {
|
||||
return n.xpath(true)
|
||||
// PartialXPathByID returns the partial XPath for the node, stopping at the
|
||||
// first parent with an id attribute or at nearest parent document node.
|
||||
func (n *Node) PartialXPathByID() string {
|
||||
return n.xpath(true, true)
|
||||
}
|
||||
|
||||
// XPath returns the full XPath tree for the node.
|
||||
func (n *Node) XPath() string {
|
||||
return n.xpath(false)
|
||||
// PartialXPath returns the partial XPath for the node, stopping at the nearest
|
||||
// parent document node.
|
||||
func (n *Node) PartialXPath() string {
|
||||
return n.xpath(true, false)
|
||||
}
|
||||
|
||||
// FullXPathByID returns the full XPath for the node, stopping at the top most
|
||||
// document root or at the closest parent node with an id attribute.
|
||||
func (n *Node) FullXPathByID() string {
|
||||
return n.xpath(false, true)
|
||||
}
|
||||
|
||||
// FullXPath returns the full XPath for the node, stopping only at the top most
|
||||
// document root.
|
||||
func (n *Node) FullXPath() string {
|
||||
return n.xpath(false, false)
|
||||
}
|
||||
|
||||
// NodeState is the state of a DOM node.
|
||||
|
|
|
@ -2798,6 +2798,8 @@ func easyjsonC5a4559bDecodeGithubComKnqChromedpCdpDebugger31(in *jlexer.Lexer, o
|
|||
out.SourceMapURL = string(in.String())
|
||||
case "hasSourceURL":
|
||||
out.HasSourceURL = bool(in.Bool())
|
||||
case "isModule":
|
||||
out.IsModule = bool(in.Bool())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
|
@ -2908,6 +2910,14 @@ func easyjsonC5a4559bEncodeGithubComKnqChromedpCdpDebugger31(out *jwriter.Writer
|
|||
out.RawString("\"hasSourceURL\":")
|
||||
out.Bool(bool(in.HasSourceURL))
|
||||
}
|
||||
if in.IsModule {
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
}
|
||||
first = false
|
||||
out.RawString("\"isModule\":")
|
||||
out.Bool(bool(in.IsModule))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
|
@ -2975,6 +2985,8 @@ func easyjsonC5a4559bDecodeGithubComKnqChromedpCdpDebugger32(in *jlexer.Lexer, o
|
|||
out.SourceMapURL = string(in.String())
|
||||
case "hasSourceURL":
|
||||
out.HasSourceURL = bool(in.Bool())
|
||||
case "isModule":
|
||||
out.IsModule = bool(in.Bool())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
|
@ -3077,6 +3089,14 @@ func easyjsonC5a4559bEncodeGithubComKnqChromedpCdpDebugger32(out *jwriter.Writer
|
|||
out.RawString("\"hasSourceURL\":")
|
||||
out.Bool(bool(in.HasSourceURL))
|
||||
}
|
||||
if in.IsModule {
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
}
|
||||
first = false
|
||||
out.RawString("\"isModule\":")
|
||||
out.Bool(bool(in.IsModule))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ type EventScriptParsed struct {
|
|||
IsLiveEdit bool `json:"isLiveEdit,omitempty"` // True, if this script is generated as a result of the live edit operation.
|
||||
SourceMapURL string `json:"sourceMapURL,omitempty"` // URL of source map associated with script (if any).
|
||||
HasSourceURL bool `json:"hasSourceURL,omitempty"` // True, if this script has sourceURL.
|
||||
IsModule bool `json:"isModule,omitempty"` // True, if this script is ES6 module.
|
||||
}
|
||||
|
||||
// EventScriptFailedToParse fired when virtual machine fails to parse the
|
||||
|
@ -39,6 +40,7 @@ type EventScriptFailedToParse struct {
|
|||
ExecutionContextAuxData easyjson.RawMessage `json:"executionContextAuxData,omitempty"`
|
||||
SourceMapURL string `json:"sourceMapURL,omitempty"` // URL of source map associated with script (if any).
|
||||
HasSourceURL bool `json:"hasSourceURL,omitempty"` // True, if this script has sourceURL.
|
||||
IsModule bool `json:"isModule,omitempty"` // True, if this script is ES6 module.
|
||||
}
|
||||
|
||||
// EventBreakpointResolved fired when breakpoint is resolved to an actual
|
||||
|
|
|
@ -184,6 +184,88 @@ func (p *GetDocumentParams) Do(ctxt context.Context, h cdp.FrameHandler) (root *
|
|||
return nil, cdp.ErrUnknownResult
|
||||
}
|
||||
|
||||
// GetFlattenedDocumentParams returns the root DOM node (and optionally the
|
||||
// subtree) to the caller.
|
||||
type GetFlattenedDocumentParams struct {
|
||||
Depth int64 `json:"depth,omitempty"` // 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.
|
||||
Pierce bool `json:"pierce,omitempty"` // Whether or not iframes and shadow roots should be traversed when returning the subtree (default is false).
|
||||
}
|
||||
|
||||
// GetFlattenedDocument returns the root DOM node (and optionally the
|
||||
// subtree) to the caller.
|
||||
//
|
||||
// parameters:
|
||||
func GetFlattenedDocument() *GetFlattenedDocumentParams {
|
||||
return &GetFlattenedDocumentParams{}
|
||||
}
|
||||
|
||||
// WithDepth 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.
|
||||
func (p GetFlattenedDocumentParams) WithDepth(depth int64) *GetFlattenedDocumentParams {
|
||||
p.Depth = depth
|
||||
return &p
|
||||
}
|
||||
|
||||
// WithPierce whether or not iframes and shadow roots should be traversed
|
||||
// when returning the subtree (default is false).
|
||||
func (p GetFlattenedDocumentParams) WithPierce(pierce bool) *GetFlattenedDocumentParams {
|
||||
p.Pierce = pierce
|
||||
return &p
|
||||
}
|
||||
|
||||
// GetFlattenedDocumentReturns return values.
|
||||
type GetFlattenedDocumentReturns struct {
|
||||
Nodes []*cdp.Node `json:"nodes,omitempty"` // Resulting node.
|
||||
}
|
||||
|
||||
// Do executes DOM.getFlattenedDocument.
|
||||
//
|
||||
// returns:
|
||||
// nodes - Resulting node.
|
||||
func (p *GetFlattenedDocumentParams) Do(ctxt context.Context, h cdp.FrameHandler) (nodes []*cdp.Node, err error) {
|
||||
if ctxt == nil {
|
||||
ctxt = context.Background()
|
||||
}
|
||||
|
||||
// marshal
|
||||
buf, err := easyjson.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// execute
|
||||
ch := h.Execute(ctxt, cdp.CommandDOMGetFlattenedDocument, easyjson.RawMessage(buf))
|
||||
|
||||
// read response
|
||||
select {
|
||||
case res := <-ch:
|
||||
if res == nil {
|
||||
return nil, cdp.ErrChannelClosed
|
||||
}
|
||||
|
||||
switch v := res.(type) {
|
||||
case easyjson.RawMessage:
|
||||
// unmarshal
|
||||
var r GetFlattenedDocumentReturns
|
||||
err = easyjson.Unmarshal(v, &r)
|
||||
if err != nil {
|
||||
return nil, cdp.ErrInvalidResult
|
||||
}
|
||||
|
||||
return r.Nodes, nil
|
||||
|
||||
case error:
|
||||
return nil, v
|
||||
}
|
||||
|
||||
case <-ctxt.Done():
|
||||
return nil, cdp.ErrContextDone
|
||||
}
|
||||
|
||||
return nil, cdp.ErrUnknownResult
|
||||
}
|
||||
|
||||
// CollectClassNamesFromSubtreeParams collects class names for the node with
|
||||
// given id and all of it's child nodes.
|
||||
type CollectClassNamesFromSubtreeParams struct {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -137,6 +137,8 @@ func easyjsonC5a4559bDecodeGithubComKnqChromedpCdp1(in *jlexer.Lexer, out *Node)
|
|||
switch key {
|
||||
case "nodeId":
|
||||
(out.NodeID).UnmarshalEasyJSON(in)
|
||||
case "parentId":
|
||||
(out.ParentID).UnmarshalEasyJSON(in)
|
||||
case "backendNodeId":
|
||||
(out.BackendNodeID).UnmarshalEasyJSON(in)
|
||||
case "nodeType":
|
||||
|
@ -352,6 +354,14 @@ func easyjsonC5a4559bEncodeGithubComKnqChromedpCdp1(out *jwriter.Writer, in Node
|
|||
out.RawString("\"nodeId\":")
|
||||
out.Int64(int64(in.NodeID))
|
||||
}
|
||||
if in.ParentID != 0 {
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
}
|
||||
first = false
|
||||
out.RawString("\"parentId\":")
|
||||
out.Int64(int64(in.ParentID))
|
||||
}
|
||||
if in.BackendNodeID != 0 {
|
||||
if !first {
|
||||
out.RawByte(',')
|
||||
|
|
|
@ -493,6 +493,9 @@ func UnmarshalMessage(msg *cdp.Message) (interface{}, error) {
|
|||
case cdp.CommandDOMGetDocument:
|
||||
v = new(dom.GetDocumentReturns)
|
||||
|
||||
case cdp.CommandDOMGetFlattenedDocument:
|
||||
v = new(dom.GetFlattenedDocumentReturns)
|
||||
|
||||
case cdp.CommandDOMCollectClassNamesFromSubtree:
|
||||
v = new(dom.CollectClassNamesFromSubtreeReturns)
|
||||
|
||||
|
|
|
@ -4328,6 +4328,13 @@
|
|||
"$ref": "NodeId",
|
||||
"description": "Node identifier that is passed into the rest of the DOM messages as the <code>nodeId</code>. Backend will only push node with given <code>id</code> once. It is aware of all requested nodes and will only fire DOM events for nodes known to the client."
|
||||
},
|
||||
{
|
||||
"name": "parentId",
|
||||
"$ref": "NodeId",
|
||||
"optional": true,
|
||||
"description": "The id of the parent node if any.",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"name": "backendNodeId",
|
||||
"$ref": "BackendNodeId",
|
||||
|
@ -4774,6 +4781,36 @@
|
|||
],
|
||||
"description": "Returns the root DOM node (and optionally the subtree) to the caller."
|
||||
},
|
||||
{
|
||||
"name": "getFlattenedDocument",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "depth",
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "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.",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"name": "pierce",
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether or not iframes and shadow roots should be traversed when returning the subtree (default is false).",
|
||||
"experimental": true
|
||||
}
|
||||
],
|
||||
"returns": [
|
||||
{
|
||||
"name": "nodes",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "Node"
|
||||
},
|
||||
"description": "Resulting node."
|
||||
}
|
||||
],
|
||||
"description": "Returns the root DOM node (and optionally the subtree) to the caller."
|
||||
},
|
||||
{
|
||||
"name": "collectClassNamesFromSubtree",
|
||||
"parameters": [
|
||||
|
@ -9526,7 +9563,9 @@
|
|||
"disabled",
|
||||
"hidden",
|
||||
"hiddenRoot",
|
||||
"invalid"
|
||||
"invalid",
|
||||
"keyshortcuts",
|
||||
"roledescription"
|
||||
],
|
||||
"description": "States which apply to every AX node."
|
||||
},
|
||||
|
@ -9566,6 +9605,7 @@
|
|||
"enum": [
|
||||
"checked",
|
||||
"expanded",
|
||||
"modal",
|
||||
"pressed",
|
||||
"selected"
|
||||
],
|
||||
|
@ -9576,9 +9616,11 @@
|
|||
"type": "string",
|
||||
"enum": [
|
||||
"activedescendant",
|
||||
"flowto",
|
||||
"controls",
|
||||
"describedby",
|
||||
"details",
|
||||
"errormessage",
|
||||
"flowto",
|
||||
"labelledby",
|
||||
"owns"
|
||||
],
|
||||
|
@ -11843,6 +11885,13 @@
|
|||
"optional": true,
|
||||
"description": "True, if this script has sourceURL.",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"name": "isModule",
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "True, if this script is ES6 module.",
|
||||
"experimental": true
|
||||
}
|
||||
],
|
||||
"description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger."
|
||||
|
@ -11908,6 +11957,13 @@
|
|||
"optional": true,
|
||||
"description": "True, if this script has sourceURL.",
|
||||
"experimental": true
|
||||
},
|
||||
{
|
||||
"name": "isModule",
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "True, if this script is ES6 module.",
|
||||
"experimental": true
|
||||
}
|
||||
],
|
||||
"description": "Fired when virtual machine fails to parse the script."
|
||||
|
|
|
@ -82,7 +82,7 @@ func (n *Node) AttributeValue(name string) string {
|
|||
}
|
||||
|
||||
// xpath builds the xpath string.
|
||||
func (n *Node) xpath(stopWhenID bool) string {
|
||||
func (n *Node) xpath(stopAtDocument, stopAtID bool) string {
|
||||
p := ""
|
||||
pos := ""
|
||||
id := n.AttributeValue("id")
|
||||
|
@ -90,7 +90,10 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
case n.Parent == nil:
|
||||
return n.LocalName
|
||||
|
||||
case stopWhenID && id != "":
|
||||
case stopAtDocument && n.NodeType == NodeTypeDocument:
|
||||
return ""
|
||||
|
||||
case stopAtID && id != "":
|
||||
p = "/"
|
||||
pos = `[@id='`+id+`']`
|
||||
|
||||
|
@ -107,7 +110,7 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
p = n.Parent.xpath(stopWhenID)
|
||||
p = n.Parent.xpath(stopAtDocument, stopAtID)
|
||||
if found {
|
||||
pos = "["+strconv.Itoa(i)+"]"
|
||||
}
|
||||
|
@ -116,15 +119,28 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
return p + "/" + n.LocalName + pos
|
||||
}
|
||||
|
||||
// XPathByID returns the XPath tree for the node, stopping at the first parent
|
||||
// with an id attribute.
|
||||
func (n *Node) XPathByID() string {
|
||||
return n.xpath(true)
|
||||
// PartialXPathByID returns the partial XPath for the node, stopping at the
|
||||
// first parent with an id attribute or at nearest parent document node.
|
||||
func (n *Node) PartialXPathByID() string {
|
||||
return n.xpath(true, true)
|
||||
}
|
||||
|
||||
// XPath returns the full XPath tree for the node.
|
||||
func (n *Node) XPath() string {
|
||||
return n.xpath(false)
|
||||
// PartialXPath returns the partial XPath for the node, stopping at the nearest
|
||||
// parent document node.
|
||||
func (n *Node) PartialXPath() string {
|
||||
return n.xpath(true, false)
|
||||
}
|
||||
|
||||
// FullXPathByID returns the full XPath for the node, stopping at the top most
|
||||
// document root or at the closest parent node with an id attribute.
|
||||
func (n *Node) FullXPathByID() string {
|
||||
return n.xpath(false, true)
|
||||
}
|
||||
|
||||
// FullXPath returns the full XPath for the node, stopping only at the top most
|
||||
// document root.
|
||||
func (n *Node) FullXPath() string {
|
||||
return n.xpath(false, false)
|
||||
}
|
||||
|
||||
// NodeState is the state of a DOM node.
|
||||
|
|
|
@ -191,7 +191,7 @@ func (n *Node) AttributeValue(name string) string {
|
|||
}
|
||||
|
||||
// xpath builds the xpath string.
|
||||
func (n *Node) xpath(stopWhenID bool) string {
|
||||
func (n *Node) xpath(stopAtDocument, stopAtID bool) string {
|
||||
p := ""
|
||||
pos := ""
|
||||
id := n.AttributeValue("id")
|
||||
|
@ -199,7 +199,10 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
case n.Parent == nil:
|
||||
return n.LocalName
|
||||
|
||||
case stopWhenID && id != "":
|
||||
case stopAtDocument && n.NodeType == NodeTypeDocument:
|
||||
return ""
|
||||
|
||||
case stopAtID && id != "":
|
||||
p = "/"
|
||||
pos = `)
|
||||
//line templates/extra.qtpl:69
|
||||
|
@ -232,7 +235,7 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
p = n.Parent.xpath(stopWhenID)
|
||||
p = n.Parent.xpath(stopAtDocument, stopAtID)
|
||||
if found {
|
||||
pos = "["+strconv.Itoa(i)+"]"
|
||||
}
|
||||
|
@ -241,15 +244,28 @@ func (n *Node) xpath(stopWhenID bool) string {
|
|||
return p + "/" + n.LocalName + pos
|
||||
}
|
||||
|
||||
// XPathByID returns the XPath tree for the node, stopping at the first parent
|
||||
// with an id attribute.
|
||||
func (n *Node) XPathByID() string {
|
||||
return n.xpath(true)
|
||||
// PartialXPathByID returns the partial XPath for the node, stopping at the
|
||||
// first parent with an id attribute or at nearest parent document node.
|
||||
func (n *Node) PartialXPathByID() string {
|
||||
return n.xpath(true, true)
|
||||
}
|
||||
|
||||
// XPath returns the full XPath tree for the node.
|
||||
func (n *Node) XPath() string {
|
||||
return n.xpath(false)
|
||||
// PartialXPath returns the partial XPath for the node, stopping at the nearest
|
||||
// parent document node.
|
||||
func (n *Node) PartialXPath() string {
|
||||
return n.xpath(true, false)
|
||||
}
|
||||
|
||||
// FullXPathByID returns the full XPath for the node, stopping at the top most
|
||||
// document root or at the closest parent node with an id attribute.
|
||||
func (n *Node) FullXPathByID() string {
|
||||
return n.xpath(false, true)
|
||||
}
|
||||
|
||||
// FullXPath returns the full XPath for the node, stopping only at the top most
|
||||
// document root.
|
||||
func (n *Node) FullXPath() string {
|
||||
return n.xpath(false, false)
|
||||
}
|
||||
|
||||
// NodeState is the state of a DOM node.
|
||||
|
@ -280,136 +296,136 @@ func (ns NodeState) String() string {
|
|||
return "[" + strings.Join(s, " ") + "]"
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
func WriteExtraNodeTemplate(qq422016 qtio422016.Writer) {
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
StreamExtraNodeTemplate(qw422016)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
func ExtraNodeTemplate() string {
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
WriteExtraNodeTemplate(qb422016)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
return qs422016
|
||||
//line templates/extra.qtpl:157
|
||||
//line templates/extra.qtpl:173
|
||||
}
|
||||
|
||||
// ExtraFixStringUnmarshaler is a template that forces values to be parsed properly.
|
||||
|
||||
//line templates/extra.qtpl:160
|
||||
//line templates/extra.qtpl:176
|
||||
func StreamExtraFixStringUnmarshaler(qw422016 *qt422016.Writer, typ, parseFunc, extra string) {
|
||||
//line templates/extra.qtpl:160
|
||||
//line templates/extra.qtpl:176
|
||||
qw422016.N().S(`
|
||||
// UnmarshalEasyJSON satisfies easyjson.Unmarshaler.
|
||||
func (t *`)
|
||||
//line templates/extra.qtpl:162
|
||||
//line templates/extra.qtpl:178
|
||||
qw422016.N().S(typ)
|
||||
//line templates/extra.qtpl:162
|
||||
//line templates/extra.qtpl:178
|
||||
qw422016.N().S(`) UnmarshalEasyJSON(in *jlexer.Lexer) {
|
||||
buf := in.Raw()
|
||||
if l := len(buf); l > 2 && buf[0] == '"' && buf[l-1] == '"' {
|
||||
buf = buf[1:l-1]
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:167
|
||||
//line templates/extra.qtpl:183
|
||||
if parseFunc != "" {
|
||||
//line templates/extra.qtpl:167
|
||||
//line templates/extra.qtpl:183
|
||||
qw422016.N().S(`
|
||||
v, err := strconv.`)
|
||||
//line templates/extra.qtpl:168
|
||||
//line templates/extra.qtpl:184
|
||||
qw422016.N().S(parseFunc)
|
||||
//line templates/extra.qtpl:168
|
||||
//line templates/extra.qtpl:184
|
||||
qw422016.N().S(`(string(buf)`)
|
||||
//line templates/extra.qtpl:168
|
||||
//line templates/extra.qtpl:184
|
||||
qw422016.N().S(extra)
|
||||
//line templates/extra.qtpl:168
|
||||
//line templates/extra.qtpl:184
|
||||
qw422016.N().S(`)
|
||||
if err != nil {
|
||||
in.AddError(err)
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:172
|
||||
//line templates/extra.qtpl:188
|
||||
}
|
||||
//line templates/extra.qtpl:172
|
||||
//line templates/extra.qtpl:188
|
||||
qw422016.N().S(`
|
||||
*t = `)
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
qw422016.N().S(typ)
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
qw422016.N().S(`(`)
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
if parseFunc != "" {
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
qw422016.N().S(`v`)
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
} else {
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
qw422016.N().S(`buf`)
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
}
|
||||
//line templates/extra.qtpl:173
|
||||
//line templates/extra.qtpl:189
|
||||
qw422016.N().S(`)
|
||||
}
|
||||
|
||||
// UnmarshalJSON satisfies json.Unmarshaler.
|
||||
func (t *`)
|
||||
//line templates/extra.qtpl:177
|
||||
//line templates/extra.qtpl:193
|
||||
qw422016.N().S(typ)
|
||||
//line templates/extra.qtpl:177
|
||||
//line templates/extra.qtpl:193
|
||||
qw422016.N().S(`) UnmarshalJSON(buf []byte) error {
|
||||
return easyjson.Unmarshal(buf, t)
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
func WriteExtraFixStringUnmarshaler(qq422016 qtio422016.Writer, typ, parseFunc, extra string) {
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
StreamExtraFixStringUnmarshaler(qw422016, typ, parseFunc, extra)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
func ExtraFixStringUnmarshaler(typ, parseFunc, extra string) string {
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
WriteExtraFixStringUnmarshaler(qb422016, typ, parseFunc, extra)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
return qs422016
|
||||
//line templates/extra.qtpl:180
|
||||
//line templates/extra.qtpl:196
|
||||
}
|
||||
|
||||
// ExtraCDPTypes is the template for additional internal type
|
||||
// declarations.
|
||||
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
func StreamExtraCDPTypes(qw422016 *qt422016.Writer) {
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
qw422016.N().S(`
|
||||
|
||||
// Error satisfies the error interface.
|
||||
|
@ -432,49 +448,49 @@ type FrameHandler interface {
|
|||
|
||||
// Empty is an empty JSON object message.
|
||||
var Empty = easyjson.RawMessage(`)
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
qw422016.N().S("`")
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
qw422016.N().S(`{}`)
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
qw422016.N().S("`")
|
||||
//line templates/extra.qtpl:184
|
||||
//line templates/extra.qtpl:200
|
||||
qw422016.N().S(`)
|
||||
`)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
func WriteExtraCDPTypes(qq422016 qtio422016.Writer) {
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
StreamExtraCDPTypes(qw422016)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
func ExtraCDPTypes() string {
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
WriteExtraCDPTypes(qb422016)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
return qs422016
|
||||
//line templates/extra.qtpl:206
|
||||
//line templates/extra.qtpl:222
|
||||
}
|
||||
|
||||
// ExtraUtilTemplate generates the decode func for the Message type.
|
||||
|
||||
//line templates/extra.qtpl:209
|
||||
//line templates/extra.qtpl:225
|
||||
func StreamExtraUtilTemplate(qw422016 *qt422016.Writer, domains []*internal.Domain) {
|
||||
//line templates/extra.qtpl:209
|
||||
//line templates/extra.qtpl:225
|
||||
qw422016.N().S(`
|
||||
type empty struct{}
|
||||
var emptyVal = &empty{}
|
||||
|
@ -483,66 +499,66 @@ var emptyVal = &empty{}
|
|||
func UnmarshalMessage(msg *cdp.Message) (interface{}, error) {
|
||||
var v easyjson.Unmarshaler
|
||||
switch msg.Method {`)
|
||||
//line templates/extra.qtpl:216
|
||||
//line templates/extra.qtpl:232
|
||||
for _, d := range domains {
|
||||
//line templates/extra.qtpl:216
|
||||
//line templates/extra.qtpl:232
|
||||
for _, c := range d.Commands {
|
||||
//line templates/extra.qtpl:216
|
||||
//line templates/extra.qtpl:232
|
||||
qw422016.N().S(`
|
||||
case cdp.`)
|
||||
//line templates/extra.qtpl:217
|
||||
//line templates/extra.qtpl:233
|
||||
qw422016.N().S(c.CommandMethodType(d))
|
||||
//line templates/extra.qtpl:217
|
||||
//line templates/extra.qtpl:233
|
||||
qw422016.N().S(`:`)
|
||||
//line templates/extra.qtpl:217
|
||||
//line templates/extra.qtpl:233
|
||||
if len(c.Returns) == 0 {
|
||||
//line templates/extra.qtpl:217
|
||||
//line templates/extra.qtpl:233
|
||||
qw422016.N().S(`
|
||||
return emptyVal, nil`)
|
||||
//line templates/extra.qtpl:218
|
||||
//line templates/extra.qtpl:234
|
||||
} else {
|
||||
//line templates/extra.qtpl:218
|
||||
//line templates/extra.qtpl:234
|
||||
qw422016.N().S(`
|
||||
v = new(`)
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
qw422016.N().S(d.PackageRefName())
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
qw422016.N().S(`.`)
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
qw422016.N().S(c.CommandReturnsType())
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
qw422016.N().S(`)`)
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
}
|
||||
//line templates/extra.qtpl:219
|
||||
//line templates/extra.qtpl:235
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line templates/extra.qtpl:220
|
||||
//line templates/extra.qtpl:236
|
||||
}
|
||||
//line templates/extra.qtpl:220
|
||||
//line templates/extra.qtpl:236
|
||||
for _, e := range d.Events {
|
||||
//line templates/extra.qtpl:220
|
||||
//line templates/extra.qtpl:236
|
||||
qw422016.N().S(`
|
||||
case cdp.`)
|
||||
//line templates/extra.qtpl:221
|
||||
//line templates/extra.qtpl:237
|
||||
qw422016.N().S(e.EventMethodType(d))
|
||||
//line templates/extra.qtpl:221
|
||||
//line templates/extra.qtpl:237
|
||||
qw422016.N().S(`:
|
||||
v = new(`)
|
||||
//line templates/extra.qtpl:222
|
||||
//line templates/extra.qtpl:238
|
||||
qw422016.N().S(d.PackageRefName())
|
||||
//line templates/extra.qtpl:222
|
||||
//line templates/extra.qtpl:238
|
||||
qw422016.N().S(`.`)
|
||||
//line templates/extra.qtpl:222
|
||||
//line templates/extra.qtpl:238
|
||||
qw422016.N().S(e.EventType())
|
||||
//line templates/extra.qtpl:222
|
||||
//line templates/extra.qtpl:238
|
||||
qw422016.N().S(`)
|
||||
`)
|
||||
//line templates/extra.qtpl:223
|
||||
//line templates/extra.qtpl:239
|
||||
}
|
||||
//line templates/extra.qtpl:223
|
||||
//line templates/extra.qtpl:239
|
||||
}
|
||||
//line templates/extra.qtpl:223
|
||||
//line templates/extra.qtpl:239
|
||||
qw422016.N().S(`}
|
||||
|
||||
var buf easyjson.RawMessage
|
||||
|
@ -565,69 +581,69 @@ func UnmarshalMessage(msg *cdp.Message) (interface{}, error) {
|
|||
return v, nil
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
func WriteExtraUtilTemplate(qq422016 qtio422016.Writer, domains []*internal.Domain) {
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
StreamExtraUtilTemplate(qw422016, domains)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
func ExtraUtilTemplate(domains []*internal.Domain) string {
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
WriteExtraUtilTemplate(qb422016, domains)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
return qs422016
|
||||
//line templates/extra.qtpl:244
|
||||
//line templates/extra.qtpl:260
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:246
|
||||
//line templates/extra.qtpl:262
|
||||
func StreamExtraMethodTypeDomainDecoder(qw422016 *qt422016.Writer) {
|
||||
//line templates/extra.qtpl:246
|
||||
//line templates/extra.qtpl:262
|
||||
qw422016.N().S(`
|
||||
// Domain returns the Chrome Debugging Protocol domain of the event or command.
|
||||
func (t MethodType) Domain() string {
|
||||
return string(t[:strings.IndexByte(string(t), '.')])
|
||||
}
|
||||
`)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
func WriteExtraMethodTypeDomainDecoder(qq422016 qtio422016.Writer) {
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
StreamExtraMethodTypeDomainDecoder(qw422016)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
}
|
||||
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
func ExtraMethodTypeDomainDecoder() string {
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
WriteExtraMethodTypeDomainDecoder(qb422016)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
qs422016 := string(qb422016.B)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
return qs422016
|
||||
//line templates/extra.qtpl:251
|
||||
//line templates/extra.qtpl:267
|
||||
}
|
||||
|
|
3
cmd/chromedp-proxy/.gitignore
vendored
Normal file
3
cmd/chromedp-proxy/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
chromedp-proxy
|
||||
chromedp-proxy.exe
|
||||
logs/
|
57
cmd/chromedp-proxy/README.md
Normal file
57
cmd/chromedp-proxy/README.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
# About chromedp-proxy
|
||||
|
||||
`chromedp-proxy` is a simple cli tool to log/intercept Chrome Debugging
|
||||
Protocol sessions, notably the websocket messages sent to/from Chrome DevTools
|
||||
and a Chromium/Chrome/headless_shell/etc. instance.
|
||||
|
||||
This is useful for finding problems/issues with the [`chromedp`](https://github.com/knq/chromedp)
|
||||
package or to review/log/capture Chrome Debugging Protocol commands, command
|
||||
results, and events sent/received by DevTools, Selenium, or any other
|
||||
application speaking the Chrome Debugging Protocol.
|
||||
|
||||
## Installation
|
||||
|
||||
Install in the usual Go way:
|
||||
|
||||
```sh
|
||||
go get -u github.com/knq/chromedp/cmd/chromedp-proxy
|
||||
```
|
||||
|
||||
## Use
|
||||
|
||||
By default, `chromedp-proxy` will listen on localhost:9223 and will proxy requests to/from localhost:9222:
|
||||
```sh
|
||||
chromedp-proxy
|
||||
```
|
||||
|
||||
`chromedp-proxy` can also be used to expose a local Chrome instance on an
|
||||
external address/port:
|
||||
```sh
|
||||
chromedp-proxy -l 192.168.1.10:9222
|
||||
```
|
||||
|
||||
By default, `chromedp-proxy` will log to both `stdout` and to `logs/cdp-<id>.log`, and can be modified using cli flags:
|
||||
```sh
|
||||
# only log to stdout
|
||||
chromedp-proxy -n
|
||||
|
||||
# another way to only log to stdout
|
||||
chromedp-proxy -log ''
|
||||
|
||||
# log to /var/log/cdp/session-<id>.log
|
||||
chromedp-proxy -log '/var/log/cdp/session-%s.log'
|
||||
```
|
||||
|
||||
## Flags:
|
||||
|
||||
```sh
|
||||
$ ./chromedp-proxy -help
|
||||
Usage of ./chromedp-proxy:
|
||||
-l string
|
||||
listen address (default "localhost:9223")
|
||||
-log string
|
||||
log file mask (default "logs/cdp-%s.log")
|
||||
-n disable logging to file
|
||||
-r string
|
||||
remote address (default "localhost:9222")
|
||||
```
|
178
cmd/chromedp-proxy/main.go
Normal file
178
cmd/chromedp-proxy/main.go
Normal file
|
@ -0,0 +1,178 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var (
|
||||
flagListen = flag.String("l", "localhost:9223", "listen address")
|
||||
flagRemote = flag.String("r", "localhost:9222", "remote address")
|
||||
flagNoLog = flag.Bool("n", false, "disable logging to file")
|
||||
flagLogMask = flag.String("log", "logs/cdp-%s.log", "log file mask")
|
||||
)
|
||||
|
||||
const (
|
||||
IncomingBufferSize = 10 * 1024 * 1024
|
||||
OutgoingBufferSize = 25 * 1024 * 1024
|
||||
)
|
||||
|
||||
var wsUpgrader = &websocket.Upgrader{
|
||||
ReadBufferSize: IncomingBufferSize,
|
||||
WriteBufferSize: OutgoingBufferSize,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
var wsDialer = &websocket.Dialer{
|
||||
ReadBufferSize: OutgoingBufferSize,
|
||||
WriteBufferSize: IncomingBufferSize,
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
simplep := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: *flagRemote})
|
||||
mux.Handle("/json", simplep)
|
||||
mux.Handle("/", simplep)
|
||||
mux.HandleFunc("/devtools/page/", func(res http.ResponseWriter, req *http.Request) {
|
||||
id := path.Base(req.URL.Path)
|
||||
f, logger := createLog(id)
|
||||
if f != nil {
|
||||
defer f.Close()
|
||||
}
|
||||
logger.Printf("---------- connection from %s ----------", req.RemoteAddr)
|
||||
|
||||
ver, err := checkVersion()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("version error, got: %v", err)
|
||||
logger.Println(msg)
|
||||
http.Error(res, msg, 500)
|
||||
return
|
||||
}
|
||||
logger.Printf("endpoint %s reported: %s", *flagRemote, string(ver))
|
||||
|
||||
endpoint := "ws://" + *flagRemote + "/devtools/page/" + id
|
||||
|
||||
// connect outgoing websocket
|
||||
logger.Printf("connecting to %s", endpoint)
|
||||
out, pres, err := wsDialer.Dial(endpoint, nil)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not connect to %s, got: %v", endpoint, err)
|
||||
logger.Println(msg)
|
||||
http.Error(res, msg, 500)
|
||||
return
|
||||
}
|
||||
defer pres.Body.Close()
|
||||
defer out.Close()
|
||||
|
||||
logger.Printf("connected to %s", endpoint)
|
||||
|
||||
// connect incoming websocket
|
||||
logger.Printf("upgrading connection on %s", req.RemoteAddr)
|
||||
in, err := wsUpgrader.Upgrade(res, req, nil)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("could not upgrade websocket from %s, got: %v", req.RemoteAddr, err)
|
||||
logger.Println(msg)
|
||||
http.Error(res, msg, 500)
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
logger.Printf("upgraded connection on %s", req.RemoteAddr)
|
||||
|
||||
ctxt, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
errc := make(chan error, 1)
|
||||
go proxyWS(ctxt, logger, "<-", in, out, errc)
|
||||
go proxyWS(ctxt, logger, "->", out, in, errc)
|
||||
<-errc
|
||||
logger.Printf("---------- closing %s ----------", req.RemoteAddr)
|
||||
})
|
||||
|
||||
log.Fatal(http.ListenAndServe(*flagListen, mux))
|
||||
}
|
||||
|
||||
func proxyWS(ctxt context.Context, logger *log.Logger, prefix string, in, out *websocket.Conn, errc chan error) {
|
||||
var mt int
|
||||
var buf []byte
|
||||
var err error
|
||||
|
||||
for {
|
||||
select {
|
||||
default:
|
||||
mt, buf, err = in.ReadMessage()
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
logger.Printf("%s %s", prefix, string(buf))
|
||||
|
||||
err = out.WriteMessage(mt, buf)
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
|
||||
case <-ctxt.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkVersion() ([]byte, error) {
|
||||
cl := &http.Client{}
|
||||
req, err := http.NewRequest("GET", "http://"+*flagRemote+"/json/version", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := cl.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var v map[string]string
|
||||
err = json.Unmarshal(body, &v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expected json result")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func createLog(id string) (io.Closer, *log.Logger) {
|
||||
var f io.Closer
|
||||
var w io.Writer = os.Stdout
|
||||
if !*flagNoLog && *flagLogMask != "" {
|
||||
l, err := os.OpenFile(fmt.Sprintf(*flagLogMask, id), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
f = l
|
||||
w = io.MultiWriter(os.Stdout, l)
|
||||
}
|
||||
return f, log.New(w, "", log.LstdFlags)
|
||||
}
|
1
examples/headless/.gitignore
vendored
1
examples/headless/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
headless
|
||||
headless.exe
|
||||
*.png
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# About headless
|
||||
|
||||
This is a version of the simple example but with the chromedp settings changed
|
||||
to use the docker [yukinying/chrome-headless](yukinying/chrome-headless) image.
|
||||
to use the docker [knqz/chrome-headless](https://hub.docker.com/r/knqz/chrome-headless/) image.
|
||||
|
||||
## Running
|
||||
|
||||
```sh
|
||||
# retrieve docker image
|
||||
docker pull yukinying/chrome-headless
|
||||
docker pull knqz/chrome-headless
|
||||
|
||||
# start docker headless
|
||||
docker run -i -t --shm-size=256m --rm --name=chrome-headless -p=127.0.0.1:9222:9222 yukinying/chrome-headless about:blank
|
||||
# start chrome-headless
|
||||
docker run -d -p 9222:9222 --rm --name chrome-headless knqz/chrome-headless
|
||||
|
||||
# run chromedp headless example
|
||||
go build && ./headless
|
||||
|
|
|
@ -42,9 +42,8 @@ func googleSearch(q, text string, site, res *string) cdp.Tasks {
|
|||
cdp.Navigate(`https://www.google.com`),
|
||||
cdp.Sleep(2 * time.Second),
|
||||
cdp.WaitVisible(`#hplogo`, cdp.ByID),
|
||||
cdp.SendKeys(`#lst-ib`, q, cdp.ByID),
|
||||
cdp.Click(`input[name="btnK"]`, cdp.ByQuery),
|
||||
cdp.WaitNotVisible(`input[name="btnI"]`, cdp.ByQuery),
|
||||
cdp.SendKeys(`#lst-ib`, q+"\n", cdp.ByID),
|
||||
cdp.WaitVisible(`#res`, cdp.ByID),
|
||||
cdp.Text(sel, res),
|
||||
cdp.Click(sel),
|
||||
cdp.Sleep(2 * time.Second),
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
cdp "github.com/knq/chromedp"
|
||||
"github.com/knq/chromedp/kb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -52,12 +53,12 @@ func sendkeys(val1, val2, val3, val4 *string) cdp.Tasks {
|
|||
cdp.Navigate("file:" + os.Getenv("GOPATH") + "/src/github.com/knq/chromedp/testdata/visible.html"),
|
||||
cdp.WaitVisible(`#input1`, cdp.ByID),
|
||||
cdp.WaitVisible(`#textarea1`, cdp.ByID),
|
||||
cdp.SendKeys(`#textarea1`, "\b\b\n\naoeu\n\ntest1\n\nblah2\n\n\t\t\t\b\bother box!\t\ntest4", cdp.ByID),
|
||||
cdp.SendKeys(`#textarea1`, kb.End+"\b\b\n\naoeu\n\ntest1\n\nblah2\n\n\t\t\t\b\bother box!\t\ntest4", cdp.ByID),
|
||||
cdp.Value(`#input1`, val1, cdp.ByID),
|
||||
cdp.Value(`#textarea1`, val2, cdp.ByID),
|
||||
cdp.SetValue(`#input2`, "test3", cdp.ByID),
|
||||
cdp.Value(`#input2`, val3, cdp.ByID),
|
||||
cdp.SendKeys(`#select1`, cdp.KeyCodeDown+cdp.KeyCodeDown, cdp.ByID),
|
||||
cdp.SendKeys(`#select1`, kb.ArrowDown+kb.ArrowDown, cdp.ByID),
|
||||
cdp.Value(`#select1`, val4, cdp.ByID),
|
||||
cdp.Sleep(30 * time.Second),
|
||||
}
|
||||
|
|
|
@ -53,9 +53,8 @@ func googleSearch(q, text string, site, res *string) cdp.Tasks {
|
|||
cdp.Navigate(`https://www.google.com`),
|
||||
cdp.Sleep(2 * time.Second),
|
||||
cdp.WaitVisible(`#hplogo`, cdp.ByID),
|
||||
cdp.SendKeys(`#lst-ib`, q, cdp.ByID),
|
||||
cdp.Click(`input[name="btnK"]`, cdp.ByQuery),
|
||||
cdp.WaitNotVisible(`input[name="btnI"]`, cdp.ByQuery),
|
||||
cdp.SendKeys(`#lst-ib`, q+"\n", cdp.ByID),
|
||||
cdp.WaitVisible(`#res`, cdp.ByID),
|
||||
cdp.Text(sel, res),
|
||||
cdp.Click(sel),
|
||||
cdp.Sleep(2 * time.Second),
|
||||
|
|
99
input.go
99
input.go
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/knq/chromedp/cdp"
|
||||
"github.com/knq/chromedp/cdp/dom"
|
||||
"github.com/knq/chromedp/cdp/input"
|
||||
"github.com/knq/chromedp/kb"
|
||||
)
|
||||
|
||||
// Error types.
|
||||
|
@ -90,96 +91,42 @@ func ClickCount(n int) MouseOption {
|
|||
}
|
||||
}
|
||||
|
||||
// KeyAction contains information about a key action.
|
||||
type KeyAction struct {
|
||||
v string
|
||||
opts []KeyOption
|
||||
}
|
||||
|
||||
// KeyCode are known system key codes.
|
||||
type KeyCode string
|
||||
|
||||
// KeyCode values.
|
||||
const (
|
||||
KeyCodeBackspace = "\b"
|
||||
KeyCodeTab = "\t"
|
||||
KeyCodeCR = "\r"
|
||||
KeyCodeLF = "\n"
|
||||
KeyCodeLeft = "\x25"
|
||||
KeyCodeUp = "\x26"
|
||||
KeyCodeRight = "\x27"
|
||||
KeyCodeDown = "\x28"
|
||||
)
|
||||
|
||||
const (
|
||||
keyRuneCR = '\r'
|
||||
)
|
||||
|
||||
// keyCodeNames is the map of key code values to their respective named
|
||||
// identifiers.
|
||||
var keyCodeNames = map[KeyCode]string{
|
||||
KeyCodeBackspace: "Backspace",
|
||||
KeyCodeTab: "Tab",
|
||||
KeyCodeCR: "Enter",
|
||||
KeyCodeLF: "Enter",
|
||||
KeyCodeLeft: "Left",
|
||||
KeyCodeUp: "Up",
|
||||
KeyCodeRight: "Right",
|
||||
KeyCodeDown: "Down",
|
||||
}
|
||||
|
||||
// Do satisfies Action interface.
|
||||
func (ka *KeyAction) Do(ctxt context.Context, h cdp.FrameHandler) error {
|
||||
// KeyAction will synthesize a keyDown, char, and keyUp event for each rune
|
||||
// contained in keys along with any supplied key options. Well known KeyCode
|
||||
// runes will not synthesize the char event.
|
||||
//
|
||||
// Note: KeyCodeCR and KeyCodeLF are exceptions to the above, and a char of
|
||||
// KeyCodeCR ('\r') will be synthesized for both.
|
||||
func KeyAction(keys string, opts ...KeyOption) Action {
|
||||
return ActionFunc(func(ctxt context.Context, h cdp.FrameHandler) error {
|
||||
var err error
|
||||
|
||||
// apply opts
|
||||
sysP := input.DispatchKeyEvent(input.KeyRawDown)
|
||||
keyP := input.DispatchKeyEvent(input.KeyChar)
|
||||
for _, o := range ka.opts {
|
||||
sysP = o(sysP)
|
||||
keyP = o(keyP)
|
||||
}
|
||||
|
||||
for _, r := range ka.v {
|
||||
s := string(r)
|
||||
keyS := KeyCode(r)
|
||||
if n, ok := keyCodeNames[keyS]; ok {
|
||||
kc := int64(r)
|
||||
if keyS == KeyCodeLF {
|
||||
s = string(keyRuneCR)
|
||||
kc = int64(keyRuneCR)
|
||||
}
|
||||
|
||||
err = sysP.WithKey(n).
|
||||
WithNativeVirtualKeyCode(kc).
|
||||
WithWindowsVirtualKeyCode(kc).
|
||||
WithKeyIdentifier(s).
|
||||
WithIsSystemKey(true).
|
||||
Do(ctxt, h)
|
||||
for _, r := range keys {
|
||||
for _, k := range kb.Encode(r) {
|
||||
err = k.Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = keyP.WithText(s).Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// TODO: move to context
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// KeyActionNode dispatches a key event on a node.
|
||||
func KeyActionNode(n *cdp.Node, v string, opts ...KeyOption) Action {
|
||||
return Tasks{
|
||||
dom.Focus(n.NodeID),
|
||||
MouseActionNode(n),
|
||||
&KeyAction{v, opts},
|
||||
func KeyActionNode(n *cdp.Node, keys string, opts ...KeyOption) Action {
|
||||
return ActionFunc(func(ctxt context.Context, h cdp.FrameHandler) error {
|
||||
err := dom.Focus(n.NodeID).Do(ctxt, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return KeyAction(keys, opts...).Do(ctxt, h)
|
||||
})
|
||||
}
|
||||
|
||||
// KeyOption is a key action option.
|
||||
|
|
560
kb/gen.go
Normal file
560
kb/gen.go
Normal file
|
@ -0,0 +1,560 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/knq/chromedp/kb"
|
||||
)
|
||||
|
||||
var (
|
||||
flagOut = flag.String("out", "keys.go", "out source")
|
||||
flagPkg = flag.String("pkg", "kb", "out package name")
|
||||
)
|
||||
|
||||
const (
|
||||
// chromiumSrc is the base chromium source repo location
|
||||
chromiumSrc = "https://chromium.googlesource.com/chromium/src"
|
||||
|
||||
// domUsLayoutDataH contains the {printable,non-printable} DomCode -> DomKey
|
||||
// also contains DomKey -> VKEY (not used)
|
||||
domUsLayoutDataH = chromiumSrc + "/+/master/ui/events/keycodes/dom_us_layout_data.h?format=TEXT"
|
||||
|
||||
// keycodeConverterDataInc contains DomKey -> Key Name
|
||||
keycodeConverterDataInc = chromiumSrc + "/+/master/ui/events/keycodes/dom/keycode_converter_data.inc?format=TEXT"
|
||||
|
||||
// domKeyDataInc contains DomKey -> Key Name + unicode (non-printable)
|
||||
domKeyDataInc = chromiumSrc + "/+/master/ui/events/keycodes/dom/dom_key_data.inc?format=TEXT"
|
||||
|
||||
// keyboardCodesPosixH contains the scan code definitions for posix (ie native) keys.
|
||||
keyboardCodesPosixH = chromiumSrc + "/+/master/ui/events/keycodes/keyboard_codes_posix.h?format=TEXT"
|
||||
|
||||
// keyboardCodesWinH contains the scan code definitions for windows keys.
|
||||
keyboardCodesWinH = chromiumSrc + "/+/master/ui/events/keycodes/keyboard_codes_win.h?format=TEXT"
|
||||
|
||||
// windowsKeyboardCodesH contains the actual #defs for windows.
|
||||
windowsKeyboardCodesH = chromiumSrc + "/third_party/+/master/WebKit/Source/platform/WindowsKeyboardCodes.h?format=TEXT"
|
||||
)
|
||||
|
||||
const (
|
||||
hdr = `package %s
|
||||
|
||||
// DOM keys.
|
||||
const (
|
||||
%s)
|
||||
|
||||
// Keys is the map of unicode characters to their DOM key data.
|
||||
var Keys = map[rune]*Key{
|
||||
%s}
|
||||
`
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// special characters
|
||||
keys := map[rune]kb.Key{
|
||||
'\b': kb.Key{"Backspace", "Backspace", "", "", int64('\b'), int64('\b'), false, false},
|
||||
'\t': kb.Key{"Tab", "Tab", "", "", int64('\t'), int64('\t'), false, false},
|
||||
'\r': kb.Key{"Enter", "Enter", "\r", "\r", int64('\r'), int64('\r'), false, true},
|
||||
}
|
||||
|
||||
// load keys
|
||||
err = loadKeys(keys)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// process keys
|
||||
constBuf, mapBuf, err := processKeys(keys)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// output
|
||||
err = ioutil.WriteFile(
|
||||
*flagOut,
|
||||
[]byte(fmt.Sprintf(hdr, *flagPkg, string(constBuf), string(mapBuf))),
|
||||
0644,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// format
|
||||
err = exec.Command("goimports", "-w", *flagOut).Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// loadKeys loads the dom key definitions from the chromium source tree.
|
||||
func loadKeys(keys map[rune]kb.Key) error {
|
||||
var err error
|
||||
|
||||
// load key converter data
|
||||
keycodeConverterMap, err := loadKeycodeConverterData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load dom code map
|
||||
domKeyMap, err := loadDomKeyData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load US layout data
|
||||
layoutBuf, err := grab(domUsLayoutDataH)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// load scan code map
|
||||
scanCodeMap, err := loadScanCodes(keycodeConverterMap, domKeyMap, layoutBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// process printable
|
||||
err = loadPrintable(keys, keycodeConverterMap, domKeyMap, layoutBuf, scanCodeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// process non-printable
|
||||
err = loadNonPrintable(keys, keycodeConverterMap, domKeyMap, layoutBuf, scanCodeMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var fixRE = regexp.MustCompile(`,\n\s{10,}`)
|
||||
var usbKeyRE = regexp.MustCompile(`(?m)^\s*USB_KEYMAP\((.*?), (.*?), (.*?), (.*?), (.*?), (.*?), (.*?)\)`)
|
||||
|
||||
// loadKeycodeConverterData loads the key codes from the keycode_converter_data.inc.
|
||||
func loadKeycodeConverterData() (map[string][]string, error) {
|
||||
buf, err := grab(keycodeConverterDataInc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = fixRE.ReplaceAllLiteral(buf, []byte(", "))
|
||||
|
||||
domMap := make(map[string][]string)
|
||||
matches := usbKeyRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
vkey := m[7]
|
||||
if _, ok := domMap[vkey]; ok {
|
||||
panic(fmt.Sprintf("vkey %s already defined", vkey))
|
||||
}
|
||||
domMap[vkey] = m[1:]
|
||||
}
|
||||
|
||||
return domMap, nil
|
||||
}
|
||||
|
||||
// decodeRune is a wrapper around parsing a printable c++ int/char definition to a unicode
|
||||
// rune value.
|
||||
func decodeRune(s string) rune {
|
||||
if strings.HasPrefix(s, "0x") {
|
||||
i, err := strconv.ParseInt(s, 0, 16)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return rune(i)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(s, "'") || !strings.HasSuffix(s, "'") {
|
||||
panic(fmt.Sprintf("expected character, got: %s", s))
|
||||
}
|
||||
|
||||
if len(s) == 4 {
|
||||
if s[1] != '\\' {
|
||||
panic(fmt.Sprintf("expected escaped character, got: %s", s))
|
||||
}
|
||||
return rune(s[2])
|
||||
}
|
||||
|
||||
if len(s) != 3 {
|
||||
panic(fmt.Sprintf("expected character, got: %s", s))
|
||||
}
|
||||
|
||||
return rune(s[1])
|
||||
}
|
||||
|
||||
// getCode is a simple wrapper around parsing the code definition.
|
||||
func getCode(s string) string {
|
||||
if !strings.HasPrefix(s, `"`) || !strings.HasSuffix(s, `"`) {
|
||||
panic(fmt.Sprintf("expected string, got: %s", s))
|
||||
}
|
||||
|
||||
return s[1 : len(s)-1]
|
||||
}
|
||||
|
||||
// addKey is a simple map add wrapper to panic if the key is already defined,
|
||||
// and to lookup the correct scan code.
|
||||
func addKey(keys map[rune]kb.Key, r rune, key kb.Key, scanCodeMap map[string][]int64, shouldPanic bool) {
|
||||
if _, ok := keys[r]; ok {
|
||||
if shouldPanic {
|
||||
panic(fmt.Sprintf("rune %U (%s/%s) already defined in keys", r, key.Code, key.Key))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sc, ok := scanCodeMap[key.Code]
|
||||
if ok {
|
||||
key.Native = sc[0]
|
||||
key.Windows = sc[1]
|
||||
}
|
||||
|
||||
keys[r] = key
|
||||
}
|
||||
|
||||
var printableKeyRE = regexp.MustCompile(`\{DomCode::(.+?), \{(.+?), (.+?)\}\}`)
|
||||
|
||||
// loadPrintable loads the printable key definitions.
|
||||
func loadPrintable(keys map[rune]kb.Key, keycodeConverterMap, domKeyMap map[string][]string, layoutBuf []byte, scanCodeMap map[string][]int64) error {
|
||||
buf := extract(layoutBuf, "kPrintableCodeMap")
|
||||
|
||||
matches := printableKeyRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
domCode := m[1]
|
||||
|
||||
// ignore domCodes that are duplicates of other unicode characters
|
||||
if domCode == "INTL_BACKSLASH" || domCode == "INTL_HASH" || strings.HasPrefix(domCode, "NUMPAD") {
|
||||
continue
|
||||
}
|
||||
|
||||
kc, ok := keycodeConverterMap[domCode]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("could not find key %s in keycode map", domCode))
|
||||
}
|
||||
|
||||
code := getCode(kc[5])
|
||||
r1, r2 := decodeRune(m[2]), decodeRune(m[3])
|
||||
addKey(keys, r1, kb.Key{
|
||||
Code: code,
|
||||
Key: string(r1),
|
||||
Text: string(r1),
|
||||
Unmodified: string(r1),
|
||||
Print: true,
|
||||
}, scanCodeMap, true)
|
||||
|
||||
// shifted value is same as non-shifted, so skip
|
||||
if r2 == r1 {
|
||||
continue
|
||||
}
|
||||
// skip for duplicate keys
|
||||
if r2 == '|' && domCode != "BACKSLASH" {
|
||||
continue
|
||||
}
|
||||
|
||||
addKey(keys, r2, kb.Key{
|
||||
Code: code,
|
||||
Key: string(r2),
|
||||
Text: string(r2),
|
||||
Unmodified: string(r1),
|
||||
Shift: true,
|
||||
Print: true,
|
||||
}, scanCodeMap, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var domKeyRE = regexp.MustCompile(`(?m)^\s+DOM_KEY_(?:UNI|MAP)\("(.+?)",\s*(.+?),\s*(0x[0-9A-F]{4})\)`)
|
||||
|
||||
// loadDomKeyData loads the dom key data definitions.
|
||||
func loadDomKeyData() (map[string][]string, error) {
|
||||
buf, err := grab(domKeyDataInc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf = fixRE.ReplaceAllLiteral(buf, []byte(", "))
|
||||
|
||||
keyMap := make(map[string][]string)
|
||||
matches := domKeyRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
keyMap[m[2]] = m[1:]
|
||||
}
|
||||
|
||||
return keyMap, nil
|
||||
}
|
||||
|
||||
var nonPrintableKeyRE = regexp.MustCompile(`\n\s{4}\{DomCode::(.+?), DomKey::(.+?)\}`)
|
||||
|
||||
// loadNonPrintable loads the not printable key definitions.
|
||||
func loadNonPrintable(keys map[rune]kb.Key, keycodeConverterMap, domKeyMap map[string][]string, layoutBuf []byte, scanCodeMap map[string][]int64) error {
|
||||
buf := extract(layoutBuf, "kNonPrintableCodeMap")
|
||||
matches := nonPrintableKeyRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
code, key := m[1], m[2]
|
||||
|
||||
// get code, key definitions
|
||||
dc, ok := keycodeConverterMap[code]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("no dom code definition for %s", code))
|
||||
}
|
||||
dk, ok := domKeyMap[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("no dom key definition for %s", key))
|
||||
}
|
||||
|
||||
// some scan codes do not have names defined, so use key name
|
||||
c := dk[0]
|
||||
if dc[5] != "NULL" {
|
||||
c = getCode(dc[5])
|
||||
}
|
||||
|
||||
// convert rune
|
||||
r, err := strconv.ParseInt(dk[2], 0, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addKey(keys, rune(r), kb.Key{
|
||||
Code: c,
|
||||
Key: dk[0],
|
||||
}, scanCodeMap, false)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var nameRE = regexp.MustCompile(`[A-Z][a-z]+:`)
|
||||
|
||||
// processKeys processes the generated keys.
|
||||
func processKeys(keys map[rune]kb.Key) ([]byte, []byte, error) {
|
||||
// order rune keys
|
||||
idx := make([]rune, len(keys))
|
||||
i := 0
|
||||
for c := range keys {
|
||||
idx[i] = c
|
||||
i++
|
||||
}
|
||||
sort.Slice(idx, func(a, b int) bool {
|
||||
return idx[a] < idx[b]
|
||||
})
|
||||
|
||||
// process
|
||||
var constBuf, mapBuf bytes.Buffer
|
||||
for _, c := range idx {
|
||||
key := keys[c]
|
||||
|
||||
g, isGoCode := goCodes[c]
|
||||
s := fmt.Sprintf("\\u%04x", c)
|
||||
if isGoCode {
|
||||
s = g
|
||||
} else if key.Print {
|
||||
s = fmt.Sprintf("%c", c)
|
||||
}
|
||||
|
||||
// add key definition
|
||||
v := strings.TrimPrefix(fmt.Sprintf("%#v", key), "kb.")
|
||||
v = nameRE.ReplaceAllString(v, "")
|
||||
mapBuf.WriteString(fmt.Sprintf("'%s': &%s,\n", s, v))
|
||||
|
||||
// fix 'Quote' const
|
||||
if s == `\'` {
|
||||
s = `'`
|
||||
}
|
||||
|
||||
// add const definition
|
||||
if (isGoCode && c != '\n') || !key.Print {
|
||||
n := strings.TrimPrefix(key.Key, ".")
|
||||
if n == `'` || n == `\` {
|
||||
n = key.Code
|
||||
}
|
||||
|
||||
constBuf.WriteString(fmt.Sprintf("%s = \"%s\"\n", n, s))
|
||||
}
|
||||
}
|
||||
|
||||
return constBuf.Bytes(), mapBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
var domCodeVkeyFixRE = regexp.MustCompile(`,\n\s{5,}`)
|
||||
var domCodeVkeyRE = regexp.MustCompile(`(?m)^\s*\{DomCode::(.+?), (.+?)\}`)
|
||||
|
||||
// loadScanCodes loads the scan codes for the dom key definitions.
|
||||
func loadScanCodes(keycodeConverterMap, domKeyMap map[string][]string, layoutBuf []byte) (map[string][]int64, error) {
|
||||
vkeyCodeMap, err := loadPosixWinKeyboardCodes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := extract(layoutBuf, "kDomCodeToKeyboardCodeMap")
|
||||
buf = domCodeVkeyFixRE.ReplaceAllLiteral(buf, []byte(", "))
|
||||
|
||||
scanCodeMap := make(map[string][]int64)
|
||||
matches := domCodeVkeyRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
domCode, vkey := m[1], m[2]
|
||||
|
||||
kc, ok := keycodeConverterMap[domCode]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dom code %s not defined in keycode map", domCode))
|
||||
}
|
||||
|
||||
sc, ok := vkeyCodeMap[vkey]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("vkey %s is not defined in keyboardCodeMap", vkey))
|
||||
}
|
||||
|
||||
scanCodeMap[getCode(kc[5])] = sc
|
||||
}
|
||||
|
||||
return scanCodeMap, nil
|
||||
}
|
||||
|
||||
var defineRE = regexp.MustCompile(`(?m)^#define\s+(.+?)\s+([0-9A-Fx]+)`)
|
||||
|
||||
// loadPosixWinKeyboardCodes loads the native and windows keyboard scan codes
|
||||
// mapped to the DOM key.
|
||||
func loadPosixWinKeyboardCodes() (map[string][]int64, error) {
|
||||
var err error
|
||||
|
||||
lookup := map[string]string{
|
||||
// mac alias
|
||||
"VKEY_LWIN": "0x5B",
|
||||
|
||||
// no idea where these are defined in chromium code base (assuming in
|
||||
// windows headers)
|
||||
//
|
||||
// manually added here as pulled from various online docs
|
||||
"VK_CANCEL": "0x03",
|
||||
"VK_OEM_ATTN": "0xF0",
|
||||
"VK_OEM_FINISH": "0xF1",
|
||||
"VK_OEM_COPY": "0xF2",
|
||||
"VK_DBE_SBCSCHAR": "0xF3",
|
||||
"VK_DBE_DBCSCHAR": "0xF4",
|
||||
"VK_OEM_BACKTAB": "0xF5",
|
||||
"VK_OEM_AX": "0xE1",
|
||||
}
|
||||
|
||||
// load windows key lookups
|
||||
buf, err := grab(windowsKeyboardCodesH)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches := defineRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
lookup[m[1]] = m[2]
|
||||
}
|
||||
|
||||
// load posix and win keyboard codes
|
||||
keyboardCodeMap := make(map[string][]int64)
|
||||
err = loadKeyboardCodes(keyboardCodeMap, lookup, keyboardCodesPosixH, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = loadKeyboardCodes(keyboardCodeMap, lookup, keyboardCodesWinH, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return keyboardCodeMap, nil
|
||||
}
|
||||
|
||||
var keyboardCodeRE = regexp.MustCompile(`(?m)^\s+(VKEY_.+?)\s+=\s+(.+?),`)
|
||||
|
||||
// loadKeyboardCodes loads the enum definition from the specified path, saving
|
||||
// the resolved symbol value to the specified position for the resulting dom
|
||||
// key name in the vkeyCodeMap.
|
||||
func loadKeyboardCodes(vkeyCodeMap map[string][]int64, lookup map[string]string, path string, pos int) error {
|
||||
buf, err := grab(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf = extract(buf, "KeyboardCode")
|
||||
|
||||
matches := keyboardCodeRE.FindAllStringSubmatch(string(buf), -1)
|
||||
for _, m := range matches {
|
||||
v := m[2]
|
||||
switch {
|
||||
case strings.HasPrefix(m[2], "'"):
|
||||
v = fmt.Sprintf("0x%04x", m[2][1])
|
||||
|
||||
case !strings.HasPrefix(m[2], "0x") && m[2] != "0":
|
||||
z, ok := lookup[v]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("could not find %s in lookup", v))
|
||||
}
|
||||
v = z
|
||||
}
|
||||
|
||||
// load the value
|
||||
i, err := strconv.ParseInt(v, 0, 32)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not parse %s // %s // %s", m[1], m[2], v))
|
||||
}
|
||||
|
||||
vkey, ok := vkeyCodeMap[m[1]]
|
||||
if !ok {
|
||||
vkey = make([]int64, 2)
|
||||
}
|
||||
vkey[pos] = i
|
||||
vkeyCodeMap[m[1]] = vkey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var endRE = regexp.MustCompile(`\n}`)
|
||||
|
||||
// extract extracts a block of next from a block of c++ code.
|
||||
func extract(buf []byte, name string) []byte {
|
||||
extractRE := regexp.MustCompile(`\s+` + name + `.+?{`)
|
||||
buf = buf[extractRE.FindIndex(buf)[0]:]
|
||||
return buf[:endRE.FindIndex(buf)[1]]
|
||||
}
|
||||
|
||||
// grab retrieves a file from the chromium source code.
|
||||
func grab(path string) ([]byte, error) {
|
||||
res, err := http.Get(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf, err := base64.StdEncoding.DecodeString(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
var goCodes = map[rune]string{
|
||||
'\a': `\a`,
|
||||
'\b': `\b`,
|
||||
'\f': `\f`,
|
||||
'\n': `\n`,
|
||||
'\r': `\r`,
|
||||
'\t': `\t`,
|
||||
'\v': `\v`,
|
||||
'\\': `\\`,
|
||||
'\'': `\'`,
|
||||
}
|
123
kb/kb.go
Normal file
123
kb/kb.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package kb
|
||||
|
||||
//go:generate go run gen.go -out keys.go -pkg kb
|
||||
|
||||
import (
|
||||
"unicode"
|
||||
|
||||
"github.com/knq/chromedp/cdp/input"
|
||||
)
|
||||
|
||||
// Key contains information for generating a key press based off the unicode
|
||||
// value.
|
||||
//
|
||||
// Example data for the following runes:
|
||||
// '\r' '\n' | ',' '<' | 'a' 'A' | '\u0a07'
|
||||
// _____________________________________________________
|
||||
type Key struct {
|
||||
// Code is the key code:
|
||||
// "Enter" | "Comma" | "KeyA" | "MediaStop"
|
||||
Code string
|
||||
|
||||
// Key is the key value:
|
||||
// "Enter" | "," "<" | "a" "A" | "MediaStop"
|
||||
Key string
|
||||
|
||||
// Text is the text for printable keys:
|
||||
// "\r" "\r" | "," "<" | "a" "A" | ""
|
||||
Text string
|
||||
|
||||
// Unmodified is the unmodified text for printable keys:
|
||||
// "\r" "\r" | "," "," | "a" "a" | ""
|
||||
Unmodified string
|
||||
|
||||
// Native is the native scan code.
|
||||
// 0x13 0x13 | 0xbc 0xbc | 0x61 0x41 | 0x00ae
|
||||
Native int64
|
||||
|
||||
// Windows is the windows scan code.
|
||||
// 0x13 0x13 | 0xbc 0xbc | 0x61 0x41 | 0xe024
|
||||
Windows int64
|
||||
|
||||
// Shift indicates whether or not the Shift modifier should be sent.
|
||||
// false false | false true | false true | false
|
||||
Shift bool
|
||||
|
||||
// Print indicates whether or not the character is a printable character
|
||||
// (ie, should a "char" event be generated).
|
||||
// true true | true true | true true | false
|
||||
Print bool
|
||||
}
|
||||
|
||||
// EncodeUnidentified encodes a keyDown, char, and keyUp sequence for an unidentified rune.
|
||||
//
|
||||
// TODO: write unit tests for non-latin/ascii unicode characters.
|
||||
func EncodeUnidentified(r rune) []*input.DispatchKeyEventParams {
|
||||
// create
|
||||
keyDown := input.DispatchKeyEventParams{
|
||||
Key: "Unidentified",
|
||||
/*NativeVirtualKeyCode: int64(r), // not sure if should be specifying the key code or not ...
|
||||
WindowsVirtualKeyCode: int64(r),*/
|
||||
}
|
||||
keyUp := keyDown
|
||||
keyDown.Type, keyUp.Type = input.KeyDown, input.KeyUp
|
||||
|
||||
// printable, so create char event
|
||||
if unicode.IsPrint(r) {
|
||||
keyChar := keyDown
|
||||
keyChar.Type = input.KeyChar
|
||||
keyChar.Text = string(r)
|
||||
keyChar.UnmodifiedText = string(r)
|
||||
|
||||
return []*input.DispatchKeyEventParams{&keyDown, &keyChar, &keyUp}
|
||||
}
|
||||
|
||||
return []*input.DispatchKeyEventParams{&keyDown, &keyUp}
|
||||
}
|
||||
|
||||
// Encode encodes a keyDown, char, and keyUp sequence for the specified rune.
|
||||
func Encode(r rune) []*input.DispatchKeyEventParams {
|
||||
// force \n -> \r
|
||||
if r == '\n' {
|
||||
r = '\r'
|
||||
}
|
||||
|
||||
// if not known key, encode as unidentified
|
||||
v, ok := Keys[r]
|
||||
if !ok {
|
||||
return EncodeUnidentified(r)
|
||||
}
|
||||
|
||||
// create
|
||||
keyDown := input.DispatchKeyEventParams{
|
||||
Key: v.Key,
|
||||
Code: v.Code,
|
||||
NativeVirtualKeyCode: v.Native,
|
||||
WindowsVirtualKeyCode: v.Windows,
|
||||
}
|
||||
if v.Shift {
|
||||
keyDown.Modifiers |= input.ModifierShift
|
||||
}
|
||||
keyUp := keyDown
|
||||
keyDown.Type, keyUp.Type = input.KeyDown, input.KeyUp
|
||||
|
||||
// printable, so create char event
|
||||
if v.Print {
|
||||
keyChar := keyDown
|
||||
keyChar.Type = input.KeyChar
|
||||
keyChar.Text = v.Text
|
||||
keyChar.UnmodifiedText = v.Unmodified
|
||||
|
||||
// the virtual key code for char events for printable characters will
|
||||
// be different than the defined keycode when not shifted...
|
||||
//
|
||||
// specifically, it always sends the ascii value as the scan code,
|
||||
// which is available as the rune.
|
||||
keyChar.NativeVirtualKeyCode = int64(r)
|
||||
keyChar.WindowsVirtualKeyCode = int64(r)
|
||||
|
||||
return []*input.DispatchKeyEventParams{&keyDown, &keyChar, &keyUp}
|
||||
}
|
||||
|
||||
return []*input.DispatchKeyEventParams{&keyDown, &keyUp}
|
||||
}
|
367
kb/keys.go
Normal file
367
kb/keys.go
Normal file
|
@ -0,0 +1,367 @@
|
|||
package kb
|
||||
|
||||
// DOM keys.
|
||||
const (
|
||||
Backspace = "\b"
|
||||
Tab = "\t"
|
||||
Enter = "\r"
|
||||
Escape = "\u001b"
|
||||
Quote = "'"
|
||||
Backslash = "\\"
|
||||
Delete = "\u007f"
|
||||
Alt = "\u0102"
|
||||
CapsLock = "\u0104"
|
||||
Control = "\u0105"
|
||||
Fn = "\u0106"
|
||||
FnLock = "\u0107"
|
||||
Hyper = "\u0108"
|
||||
Meta = "\u0109"
|
||||
NumLock = "\u010a"
|
||||
ScrollLock = "\u010c"
|
||||
Shift = "\u010d"
|
||||
Super = "\u010e"
|
||||
ArrowDown = "\u0301"
|
||||
ArrowLeft = "\u0302"
|
||||
ArrowRight = "\u0303"
|
||||
ArrowUp = "\u0304"
|
||||
End = "\u0305"
|
||||
Home = "\u0306"
|
||||
PageDown = "\u0307"
|
||||
PageUp = "\u0308"
|
||||
Clear = "\u0401"
|
||||
Copy = "\u0402"
|
||||
Cut = "\u0404"
|
||||
Insert = "\u0407"
|
||||
Paste = "\u0408"
|
||||
Redo = "\u0409"
|
||||
Undo = "\u040a"
|
||||
Again = "\u0502"
|
||||
Cancel = "\u0504"
|
||||
ContextMenu = "\u0505"
|
||||
Find = "\u0507"
|
||||
Help = "\u0508"
|
||||
Pause = "\u0509"
|
||||
Props = "\u050b"
|
||||
Select = "\u050c"
|
||||
ZoomIn = "\u050d"
|
||||
ZoomOut = "\u050e"
|
||||
BrightnessDown = "\u0601"
|
||||
BrightnessUp = "\u0602"
|
||||
Eject = "\u0604"
|
||||
LogOff = "\u0605"
|
||||
Power = "\u0606"
|
||||
PrintScreen = "\u0608"
|
||||
WakeUp = "\u060b"
|
||||
Convert = "\u0705"
|
||||
NonConvert = "\u070d"
|
||||
HangulMode = "\u0711"
|
||||
HanjaMode = "\u0712"
|
||||
Hiragana = "\u0716"
|
||||
KanaMode = "\u0718"
|
||||
Katakana = "\u071a"
|
||||
ZenkakuHankaku = "\u071d"
|
||||
F1 = "\u0801"
|
||||
F2 = "\u0802"
|
||||
F3 = "\u0803"
|
||||
F4 = "\u0804"
|
||||
F5 = "\u0805"
|
||||
F6 = "\u0806"
|
||||
F7 = "\u0807"
|
||||
F8 = "\u0808"
|
||||
F9 = "\u0809"
|
||||
F10 = "\u080a"
|
||||
F11 = "\u080b"
|
||||
F12 = "\u080c"
|
||||
F13 = "\u080d"
|
||||
F14 = "\u080e"
|
||||
F15 = "\u080f"
|
||||
F16 = "\u0810"
|
||||
F17 = "\u0811"
|
||||
F18 = "\u0812"
|
||||
F19 = "\u0813"
|
||||
F20 = "\u0814"
|
||||
F21 = "\u0815"
|
||||
F22 = "\u0816"
|
||||
F23 = "\u0817"
|
||||
F24 = "\u0818"
|
||||
Close = "\u0a01"
|
||||
MailForward = "\u0a02"
|
||||
MailReply = "\u0a03"
|
||||
MailSend = "\u0a04"
|
||||
MediaPlayPause = "\u0a05"
|
||||
MediaStop = "\u0a07"
|
||||
MediaTrackNext = "\u0a08"
|
||||
MediaTrackPrevious = "\u0a09"
|
||||
New = "\u0a0a"
|
||||
Open = "\u0a0b"
|
||||
Print = "\u0a0c"
|
||||
Save = "\u0a0d"
|
||||
SpellCheck = "\u0a0e"
|
||||
AudioVolumeDown = "\u0a0f"
|
||||
AudioVolumeUp = "\u0a10"
|
||||
AudioVolumeMute = "\u0a11"
|
||||
LaunchCalculator = "\u0b01"
|
||||
LaunchCalendar = "\u0b02"
|
||||
LaunchMail = "\u0b03"
|
||||
LaunchMediaPlayer = "\u0b04"
|
||||
LaunchMusicPlayer = "\u0b05"
|
||||
LaunchMyComputer = "\u0b06"
|
||||
LaunchScreenSaver = "\u0b07"
|
||||
LaunchSpreadsheet = "\u0b08"
|
||||
LaunchWebBrowser = "\u0b09"
|
||||
LaunchContacts = "\u0b0c"
|
||||
LaunchPhone = "\u0b0d"
|
||||
BrowserBack = "\u0c01"
|
||||
BrowserFavorites = "\u0c02"
|
||||
BrowserForward = "\u0c03"
|
||||
BrowserHome = "\u0c04"
|
||||
BrowserRefresh = "\u0c05"
|
||||
BrowserSearch = "\u0c06"
|
||||
BrowserStop = "\u0c07"
|
||||
ChannelDown = "\u0d0a"
|
||||
ChannelUp = "\u0d0b"
|
||||
ClosedCaptionToggle = "\u0d12"
|
||||
Exit = "\u0d15"
|
||||
Guide = "\u0d22"
|
||||
Info = "\u0d25"
|
||||
MediaFastForward = "\u0d2c"
|
||||
MediaLast = "\u0d2d"
|
||||
MediaPlay = "\u0d2f"
|
||||
MediaRecord = "\u0d30"
|
||||
MediaRewind = "\u0d31"
|
||||
Settings = "\u0d43"
|
||||
ZoomToggle = "\u0d4e"
|
||||
AudioBassBoostToggle = "\u0e02"
|
||||
SpeechInputToggle = "\u0f02"
|
||||
AppSwitch = "\u1001"
|
||||
)
|
||||
|
||||
// Keys is the map of unicode characters to their DOM key data.
|
||||
var Keys = map[rune]*Key{
|
||||
'\b': &Key{"Backspace", "Backspace", "", "", 8, 8, false, false},
|
||||
'\t': &Key{"Tab", "Tab", "", "", 9, 9, false, false},
|
||||
'\r': &Key{"Enter", "Enter", "\r", "\r", 13, 13, false, true},
|
||||
'\u001b': &Key{"Escape", "Escape", "", "", 27, 27, false, false},
|
||||
' ': &Key{"Space", " ", " ", " ", 32, 32, false, true},
|
||||
'!': &Key{"Digit1", "!", "!", "1", 49, 49, true, true},
|
||||
'"': &Key{"Quote", "\"", "\"", "'", 222, 222, true, true},
|
||||
'#': &Key{"Digit3", "#", "#", "3", 51, 51, true, true},
|
||||
'$': &Key{"Digit4", "$", "$", "4", 52, 52, true, true},
|
||||
'%': &Key{"Digit5", "%", "%", "5", 53, 53, true, true},
|
||||
'&': &Key{"Digit7", "&", "&", "7", 55, 55, true, true},
|
||||
'\'': &Key{"Quote", "'", "'", "'", 222, 222, false, true},
|
||||
'(': &Key{"Digit9", "(", "(", "9", 57, 57, true, true},
|
||||
')': &Key{"Digit0", ")", ")", "0", 48, 48, true, true},
|
||||
'*': &Key{"Digit8", "*", "*", "8", 56, 56, true, true},
|
||||
'+': &Key{"Equal", "+", "+", "=", 187, 187, true, true},
|
||||
',': &Key{"Comma", ",", ",", ",", 188, 188, false, true},
|
||||
'-': &Key{"Minus", "-", "-", "-", 189, 189, false, true},
|
||||
'.': &Key{"Period", ".", ".", ".", 190, 190, false, true},
|
||||
'/': &Key{"Slash", "/", "/", "/", 191, 191, false, true},
|
||||
'0': &Key{"Digit0", "0", "0", "0", 48, 48, false, true},
|
||||
'1': &Key{"Digit1", "1", "1", "1", 49, 49, false, true},
|
||||
'2': &Key{"Digit2", "2", "2", "2", 50, 50, false, true},
|
||||
'3': &Key{"Digit3", "3", "3", "3", 51, 51, false, true},
|
||||
'4': &Key{"Digit4", "4", "4", "4", 52, 52, false, true},
|
||||
'5': &Key{"Digit5", "5", "5", "5", 53, 53, false, true},
|
||||
'6': &Key{"Digit6", "6", "6", "6", 54, 54, false, true},
|
||||
'7': &Key{"Digit7", "7", "7", "7", 55, 55, false, true},
|
||||
'8': &Key{"Digit8", "8", "8", "8", 56, 56, false, true},
|
||||
'9': &Key{"Digit9", "9", "9", "9", 57, 57, false, true},
|
||||
':': &Key{"Semicolon", ":", ":", ";", 186, 186, true, true},
|
||||
';': &Key{"Semicolon", ";", ";", ";", 186, 186, false, true},
|
||||
'<': &Key{"Comma", "<", "<", ",", 188, 188, true, true},
|
||||
'=': &Key{"Equal", "=", "=", "=", 187, 187, false, true},
|
||||
'>': &Key{"Period", ">", ">", ".", 190, 190, true, true},
|
||||
'?': &Key{"Slash", "?", "?", "/", 191, 191, true, true},
|
||||
'@': &Key{"Digit2", "@", "@", "2", 50, 50, true, true},
|
||||
'A': &Key{"KeyA", "A", "A", "a", 65, 65, true, true},
|
||||
'B': &Key{"KeyB", "B", "B", "b", 66, 66, true, true},
|
||||
'C': &Key{"KeyC", "C", "C", "c", 67, 67, true, true},
|
||||
'D': &Key{"KeyD", "D", "D", "d", 68, 68, true, true},
|
||||
'E': &Key{"KeyE", "E", "E", "e", 69, 69, true, true},
|
||||
'F': &Key{"KeyF", "F", "F", "f", 70, 70, true, true},
|
||||
'G': &Key{"KeyG", "G", "G", "g", 71, 71, true, true},
|
||||
'H': &Key{"KeyH", "H", "H", "h", 72, 72, true, true},
|
||||
'I': &Key{"KeyI", "I", "I", "i", 73, 73, true, true},
|
||||
'J': &Key{"KeyJ", "J", "J", "j", 74, 74, true, true},
|
||||
'K': &Key{"KeyK", "K", "K", "k", 75, 75, true, true},
|
||||
'L': &Key{"KeyL", "L", "L", "l", 76, 76, true, true},
|
||||
'M': &Key{"KeyM", "M", "M", "m", 77, 77, true, true},
|
||||
'N': &Key{"KeyN", "N", "N", "n", 78, 78, true, true},
|
||||
'O': &Key{"KeyO", "O", "O", "o", 79, 79, true, true},
|
||||
'P': &Key{"KeyP", "P", "P", "p", 80, 80, true, true},
|
||||
'Q': &Key{"KeyQ", "Q", "Q", "q", 81, 81, true, true},
|
||||
'R': &Key{"KeyR", "R", "R", "r", 82, 82, true, true},
|
||||
'S': &Key{"KeyS", "S", "S", "s", 83, 83, true, true},
|
||||
'T': &Key{"KeyT", "T", "T", "t", 84, 84, true, true},
|
||||
'U': &Key{"KeyU", "U", "U", "u", 85, 85, true, true},
|
||||
'V': &Key{"KeyV", "V", "V", "v", 86, 86, true, true},
|
||||
'W': &Key{"KeyW", "W", "W", "w", 87, 87, true, true},
|
||||
'X': &Key{"KeyX", "X", "X", "x", 88, 88, true, true},
|
||||
'Y': &Key{"KeyY", "Y", "Y", "y", 89, 89, true, true},
|
||||
'Z': &Key{"KeyZ", "Z", "Z", "z", 90, 90, true, true},
|
||||
'[': &Key{"BracketLeft", "[", "[", "[", 219, 219, false, true},
|
||||
'\\': &Key{"Backslash", "\\", "\\", "\\", 220, 220, false, true},
|
||||
']': &Key{"BracketRight", "]", "]", "]", 221, 221, false, true},
|
||||
'^': &Key{"Digit6", "^", "^", "6", 54, 54, true, true},
|
||||
'_': &Key{"Minus", "_", "_", "-", 189, 189, true, true},
|
||||
'`': &Key{"Backquote", "`", "`", "`", 192, 192, false, true},
|
||||
'a': &Key{"KeyA", "a", "a", "a", 65, 65, false, true},
|
||||
'b': &Key{"KeyB", "b", "b", "b", 66, 66, false, true},
|
||||
'c': &Key{"KeyC", "c", "c", "c", 67, 67, false, true},
|
||||
'd': &Key{"KeyD", "d", "d", "d", 68, 68, false, true},
|
||||
'e': &Key{"KeyE", "e", "e", "e", 69, 69, false, true},
|
||||
'f': &Key{"KeyF", "f", "f", "f", 70, 70, false, true},
|
||||
'g': &Key{"KeyG", "g", "g", "g", 71, 71, false, true},
|
||||
'h': &Key{"KeyH", "h", "h", "h", 72, 72, false, true},
|
||||
'i': &Key{"KeyI", "i", "i", "i", 73, 73, false, true},
|
||||
'j': &Key{"KeyJ", "j", "j", "j", 74, 74, false, true},
|
||||
'k': &Key{"KeyK", "k", "k", "k", 75, 75, false, true},
|
||||
'l': &Key{"KeyL", "l", "l", "l", 76, 76, false, true},
|
||||
'm': &Key{"KeyM", "m", "m", "m", 77, 77, false, true},
|
||||
'n': &Key{"KeyN", "n", "n", "n", 78, 78, false, true},
|
||||
'o': &Key{"KeyO", "o", "o", "o", 79, 79, false, true},
|
||||
'p': &Key{"KeyP", "p", "p", "p", 80, 80, false, true},
|
||||
'q': &Key{"KeyQ", "q", "q", "q", 81, 81, false, true},
|
||||
'r': &Key{"KeyR", "r", "r", "r", 82, 82, false, true},
|
||||
's': &Key{"KeyS", "s", "s", "s", 83, 83, false, true},
|
||||
't': &Key{"KeyT", "t", "t", "t", 84, 84, false, true},
|
||||
'u': &Key{"KeyU", "u", "u", "u", 85, 85, false, true},
|
||||
'v': &Key{"KeyV", "v", "v", "v", 86, 86, false, true},
|
||||
'w': &Key{"KeyW", "w", "w", "w", 87, 87, false, true},
|
||||
'x': &Key{"KeyX", "x", "x", "x", 88, 88, false, true},
|
||||
'y': &Key{"KeyY", "y", "y", "y", 89, 89, false, true},
|
||||
'z': &Key{"KeyZ", "z", "z", "z", 90, 90, false, true},
|
||||
'{': &Key{"BracketLeft", "{", "{", "[", 219, 219, true, true},
|
||||
'|': &Key{"Backslash", "|", "|", "\\", 220, 220, true, true},
|
||||
'}': &Key{"BracketRight", "}", "}", "]", 221, 221, true, true},
|
||||
'~': &Key{"Backquote", "~", "~", "`", 192, 192, true, true},
|
||||
'\u007f': &Key{"Delete", "Delete", "", "", 46, 46, false, false},
|
||||
'¥': &Key{"IntlYen", "¥", "¥", "¥", 220, 220, false, true},
|
||||
'\u0102': &Key{"AltLeft", "Alt", "", "", 164, 164, false, false},
|
||||
'\u0104': &Key{"CapsLock", "CapsLock", "", "", 20, 20, false, false},
|
||||
'\u0105': &Key{"ControlLeft", "Control", "", "", 162, 162, false, false},
|
||||
'\u0106': &Key{"Fn", "Fn", "", "", 0, 0, false, false},
|
||||
'\u0107': &Key{"FnLock", "FnLock", "", "", 0, 0, false, false},
|
||||
'\u0108': &Key{"Hyper", "Hyper", "", "", 0, 0, false, false},
|
||||
'\u0109': &Key{"MetaLeft", "Meta", "", "", 91, 91, false, false},
|
||||
'\u010a': &Key{"NumLock", "NumLock", "", "", 144, 144, false, false},
|
||||
'\u010c': &Key{"ScrollLock", "ScrollLock", "", "", 145, 145, false, false},
|
||||
'\u010d': &Key{"ShiftLeft", "Shift", "", "", 160, 160, false, false},
|
||||
'\u010e': &Key{"Super", "Super", "", "", 0, 0, false, false},
|
||||
'\u0301': &Key{"ArrowDown", "ArrowDown", "", "", 40, 40, false, false},
|
||||
'\u0302': &Key{"ArrowLeft", "ArrowLeft", "", "", 37, 37, false, false},
|
||||
'\u0303': &Key{"ArrowRight", "ArrowRight", "", "", 39, 39, false, false},
|
||||
'\u0304': &Key{"ArrowUp", "ArrowUp", "", "", 38, 38, false, false},
|
||||
'\u0305': &Key{"End", "End", "", "", 35, 35, false, false},
|
||||
'\u0306': &Key{"Home", "Home", "", "", 36, 36, false, false},
|
||||
'\u0307': &Key{"PageDown", "PageDown", "", "", 34, 34, false, false},
|
||||
'\u0308': &Key{"PageUp", "PageUp", "", "", 33, 33, false, false},
|
||||
'\u0401': &Key{"NumpadClear", "Clear", "", "", 12, 12, false, false},
|
||||
'\u0402': &Key{"Copy", "Copy", "", "", 0, 0, false, false},
|
||||
'\u0404': &Key{"Cut", "Cut", "", "", 0, 0, false, false},
|
||||
'\u0407': &Key{"Insert", "Insert", "", "", 45, 45, false, false},
|
||||
'\u0408': &Key{"Paste", "Paste", "", "", 0, 0, false, false},
|
||||
'\u0409': &Key{"Redo", "Redo", "", "", 0, 0, false, false},
|
||||
'\u040a': &Key{"Undo", "Undo", "", "", 0, 0, false, false},
|
||||
'\u0502': &Key{"Again", "Again", "", "", 0, 0, false, false},
|
||||
'\u0504': &Key{"Abort", "Cancel", "", "", 0, 0, false, false},
|
||||
'\u0505': &Key{"ContextMenu", "ContextMenu", "", "", 93, 93, false, false},
|
||||
'\u0507': &Key{"Find", "Find", "", "", 0, 0, false, false},
|
||||
'\u0508': &Key{"Help", "Help", "", "", 47, 47, false, false},
|
||||
'\u0509': &Key{"Pause", "Pause", "", "", 19, 19, false, false},
|
||||
'\u050b': &Key{"Props", "Props", "", "", 0, 0, false, false},
|
||||
'\u050c': &Key{"Select", "Select", "", "", 41, 41, false, false},
|
||||
'\u050d': &Key{"ZoomIn", "ZoomIn", "", "", 0, 0, false, false},
|
||||
'\u050e': &Key{"ZoomOut", "ZoomOut", "", "", 0, 0, false, false},
|
||||
'\u0601': &Key{"BrightnessDown", "BrightnessDown", "", "", 216, 0, false, false},
|
||||
'\u0602': &Key{"BrightnessUp", "BrightnessUp", "", "", 217, 0, false, false},
|
||||
'\u0604': &Key{"Eject", "Eject", "", "", 0, 0, false, false},
|
||||
'\u0605': &Key{"LogOff", "LogOff", "", "", 0, 0, false, false},
|
||||
'\u0606': &Key{"Power", "Power", "", "", 152, 0, false, false},
|
||||
'\u0608': &Key{"PrintScreen", "PrintScreen", "", "", 44, 44, false, false},
|
||||
'\u060b': &Key{"WakeUp", "WakeUp", "", "", 0, 0, false, false},
|
||||
'\u0705': &Key{"Convert", "Convert", "", "", 28, 28, false, false},
|
||||
'\u070d': &Key{"NonConvert", "NonConvert", "", "", 29, 29, false, false},
|
||||
'\u0711': &Key{"Lang1", "HangulMode", "", "", 21, 21, false, false},
|
||||
'\u0712': &Key{"Lang2", "HanjaMode", "", "", 25, 25, false, false},
|
||||
'\u0716': &Key{"Lang4", "Hiragana", "", "", 0, 0, false, false},
|
||||
'\u0718': &Key{"KanaMode", "KanaMode", "", "", 21, 21, false, false},
|
||||
'\u071a': &Key{"Lang3", "Katakana", "", "", 0, 0, false, false},
|
||||
'\u071d': &Key{"Lang5", "ZenkakuHankaku", "", "", 0, 0, false, false},
|
||||
'\u0801': &Key{"F1", "F1", "", "", 112, 112, false, false},
|
||||
'\u0802': &Key{"F2", "F2", "", "", 113, 113, false, false},
|
||||
'\u0803': &Key{"F3", "F3", "", "", 114, 114, false, false},
|
||||
'\u0804': &Key{"F4", "F4", "", "", 115, 115, false, false},
|
||||
'\u0805': &Key{"F5", "F5", "", "", 116, 116, false, false},
|
||||
'\u0806': &Key{"F6", "F6", "", "", 117, 117, false, false},
|
||||
'\u0807': &Key{"F7", "F7", "", "", 118, 118, false, false},
|
||||
'\u0808': &Key{"F8", "F8", "", "", 119, 119, false, false},
|
||||
'\u0809': &Key{"F9", "F9", "", "", 120, 120, false, false},
|
||||
'\u080a': &Key{"F10", "F10", "", "", 121, 121, false, false},
|
||||
'\u080b': &Key{"F11", "F11", "", "", 122, 122, false, false},
|
||||
'\u080c': &Key{"F12", "F12", "", "", 123, 123, false, false},
|
||||
'\u080d': &Key{"F13", "F13", "", "", 124, 124, false, false},
|
||||
'\u080e': &Key{"F14", "F14", "", "", 125, 125, false, false},
|
||||
'\u080f': &Key{"F15", "F15", "", "", 126, 126, false, false},
|
||||
'\u0810': &Key{"F16", "F16", "", "", 127, 127, false, false},
|
||||
'\u0811': &Key{"F17", "F17", "", "", 128, 128, false, false},
|
||||
'\u0812': &Key{"F18", "F18", "", "", 129, 129, false, false},
|
||||
'\u0813': &Key{"F19", "F19", "", "", 130, 130, false, false},
|
||||
'\u0814': &Key{"F20", "F20", "", "", 131, 131, false, false},
|
||||
'\u0815': &Key{"F21", "F21", "", "", 132, 132, false, false},
|
||||
'\u0816': &Key{"F22", "F22", "", "", 133, 133, false, false},
|
||||
'\u0817': &Key{"F23", "F23", "", "", 134, 134, false, false},
|
||||
'\u0818': &Key{"F24", "F24", "", "", 135, 135, false, false},
|
||||
'\u0a01': &Key{"Close", "Close", "", "", 0, 0, false, false},
|
||||
'\u0a02': &Key{"MailForward", "MailForward", "", "", 0, 0, false, false},
|
||||
'\u0a03': &Key{"MailReply", "MailReply", "", "", 0, 0, false, false},
|
||||
'\u0a04': &Key{"MailSend", "MailSend", "", "", 0, 0, false, false},
|
||||
'\u0a05': &Key{"MediaPlayPause", "MediaPlayPause", "", "", 179, 179, false, false},
|
||||
'\u0a07': &Key{"MediaStop", "MediaStop", "", "", 178, 178, false, false},
|
||||
'\u0a08': &Key{"MediaTrackNext", "MediaTrackNext", "", "", 176, 176, false, false},
|
||||
'\u0a09': &Key{"MediaTrackPrevious", "MediaTrackPrevious", "", "", 177, 177, false, false},
|
||||
'\u0a0a': &Key{"New", "New", "", "", 0, 0, false, false},
|
||||
'\u0a0b': &Key{"Open", "Open", "", "", 43, 43, false, false},
|
||||
'\u0a0c': &Key{"Print", "Print", "", "", 0, 0, false, false},
|
||||
'\u0a0d': &Key{"Save", "Save", "", "", 0, 0, false, false},
|
||||
'\u0a0e': &Key{"SpellCheck", "SpellCheck", "", "", 0, 0, false, false},
|
||||
'\u0a0f': &Key{"AudioVolumeDown", "AudioVolumeDown", "", "", 174, 174, false, false},
|
||||
'\u0a10': &Key{"AudioVolumeUp", "AudioVolumeUp", "", "", 175, 175, false, false},
|
||||
'\u0a11': &Key{"AudioVolumeMute", "AudioVolumeMute", "", "", 173, 173, false, false},
|
||||
'\u0b01': &Key{"LaunchApp2", "LaunchCalculator", "", "", 183, 183, false, false},
|
||||
'\u0b02': &Key{"LaunchCalendar", "LaunchCalendar", "", "", 0, 0, false, false},
|
||||
'\u0b03': &Key{"LaunchMail", "LaunchMail", "", "", 180, 180, false, false},
|
||||
'\u0b04': &Key{"MediaSelect", "LaunchMediaPlayer", "", "", 181, 181, false, false},
|
||||
'\u0b05': &Key{"LaunchMusicPlayer", "LaunchMusicPlayer", "", "", 0, 0, false, false},
|
||||
'\u0b06': &Key{"LaunchApp1", "LaunchMyComputer", "", "", 182, 182, false, false},
|
||||
'\u0b07': &Key{"LaunchScreenSaver", "LaunchScreenSaver", "", "", 0, 0, false, false},
|
||||
'\u0b08': &Key{"LaunchSpreadsheet", "LaunchSpreadsheet", "", "", 0, 0, false, false},
|
||||
'\u0b09': &Key{"LaunchWebBrowser", "LaunchWebBrowser", "", "", 0, 0, false, false},
|
||||
'\u0b0c': &Key{"LaunchContacts", "LaunchContacts", "", "", 0, 0, false, false},
|
||||
'\u0b0d': &Key{"LaunchPhone", "LaunchPhone", "", "", 0, 0, false, false},
|
||||
'\u0c01': &Key{"BrowserBack", "BrowserBack", "", "", 166, 166, false, false},
|
||||
'\u0c02': &Key{"BrowserFavorites", "BrowserFavorites", "", "", 171, 171, false, false},
|
||||
'\u0c03': &Key{"BrowserForward", "BrowserForward", "", "", 167, 167, false, false},
|
||||
'\u0c04': &Key{"BrowserHome", "BrowserHome", "", "", 172, 172, false, false},
|
||||
'\u0c05': &Key{"BrowserRefresh", "BrowserRefresh", "", "", 168, 168, false, false},
|
||||
'\u0c06': &Key{"BrowserSearch", "BrowserSearch", "", "", 170, 170, false, false},
|
||||
'\u0c07': &Key{"BrowserStop", "BrowserStop", "", "", 169, 169, false, false},
|
||||
'\u0d0a': &Key{"ChannelDown", "ChannelDown", "", "", 0, 0, false, false},
|
||||
'\u0d0b': &Key{"ChannelUp", "ChannelUp", "", "", 0, 0, false, false},
|
||||
'\u0d12': &Key{"ClosedCaptionToggle", "ClosedCaptionToggle", "", "", 0, 0, false, false},
|
||||
'\u0d15': &Key{"Exit", "Exit", "", "", 0, 0, false, false},
|
||||
'\u0d22': &Key{"Guide", "Guide", "", "", 0, 0, false, false},
|
||||
'\u0d25': &Key{"Info", "Info", "", "", 0, 0, false, false},
|
||||
'\u0d2c': &Key{"MediaFastForward", "MediaFastForward", "", "", 0, 0, false, false},
|
||||
'\u0d2d': &Key{"MediaLast", "MediaLast", "", "", 0, 0, false, false},
|
||||
'\u0d2f': &Key{"MediaPlay", "MediaPlay", "", "", 0, 0, false, false},
|
||||
'\u0d30': &Key{"MediaRecord", "MediaRecord", "", "", 0, 0, false, false},
|
||||
'\u0d31': &Key{"MediaRewind", "MediaRewind", "", "", 0, 0, false, false},
|
||||
'\u0d43': &Key{"Settings", "Settings", "", "", 0, 0, false, false},
|
||||
'\u0d4e': &Key{"ZoomToggle", "ZoomToggle", "", "", 251, 251, false, false},
|
||||
'\u0e02': &Key{"AudioBassBoostToggle", "AudioBassBoostToggle", "", "", 0, 0, false, false},
|
||||
'\u0f02': &Key{"SpeechInputToggle", "SpeechInputToggle", "", "", 0, 0, false, false},
|
||||
'\u1001': &Key{"SelectTask", "AppSwitch", "", "", 0, 0, false, false},
|
||||
}
|
8
query.go
8
query.go
|
@ -133,7 +133,7 @@ func Value(sel interface{}, value *string, opts ...QueryOption) Action {
|
|||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||
}
|
||||
|
||||
p := rundom.Evaluate(fmt.Sprintf(valueJS, nodes[0].XPath()))
|
||||
p := rundom.Evaluate(fmt.Sprintf(valueJS, nodes[0].FullXPath()))
|
||||
p.IncludeCommandLineAPI = true
|
||||
p.ObjectGroup = "console"
|
||||
|
||||
|
@ -161,7 +161,7 @@ func SetValue(sel interface{}, value string, opts ...QueryOption) Action {
|
|||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||
}
|
||||
|
||||
p := rundom.Evaluate(fmt.Sprintf(setValueJS, nodes[0].XPath(), value))
|
||||
p := rundom.Evaluate(fmt.Sprintf(setValueJS, nodes[0].FullXPath(), value))
|
||||
p.IncludeCommandLineAPI = true
|
||||
p.ObjectGroup = "console"
|
||||
|
||||
|
@ -190,7 +190,7 @@ func Text(sel interface{}, text *string, opts ...QueryOption) Action {
|
|||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||
}
|
||||
|
||||
p := rundom.Evaluate(fmt.Sprintf(textJS, nodes[0].XPath()))
|
||||
p := rundom.Evaluate(fmt.Sprintf(textJS, nodes[0].FullXPath()))
|
||||
p.IncludeCommandLineAPI = true
|
||||
p.ObjectGroup = "console"
|
||||
|
||||
|
@ -429,7 +429,7 @@ func Submit(sel interface{}, opts ...QueryOption) Action {
|
|||
return fmt.Errorf("selector `%s` did not return any nodes", sel)
|
||||
}
|
||||
|
||||
p := rundom.Evaluate(fmt.Sprintf(submitJS, nodes[0].XPath()))
|
||||
p := rundom.Evaluate(fmt.Sprintf(submitJS, nodes[0].FullXPath()))
|
||||
p.IncludeCommandLineAPI = true
|
||||
p.ObjectGroup = "console"
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user