564 lines
16 KiB
Go
564 lines
16 KiB
Go
|
package fixup
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
|
||
|
. "github.com/knq/chromedp/cmd/chromedp-gen/internal"
|
||
|
"github.com/knq/chromedp/cmd/chromedp-gen/templates"
|
||
|
)
|
||
|
|
||
|
func init() {
|
||
|
// set the internal types
|
||
|
SetInternalTypes(map[string]bool{
|
||
|
"CSS.ComputedProperty": true,
|
||
|
"DOM.BackendNodeId": true,
|
||
|
"DOM.BackendNode": true,
|
||
|
"DOM.NodeId": true,
|
||
|
"DOM.Node": true,
|
||
|
"DOM.NodeType": true,
|
||
|
"DOM.PseudoType": true,
|
||
|
"DOM.RGBA": true,
|
||
|
"DOM.ShadowRootType": true,
|
||
|
"Inspector.ErrorType": true,
|
||
|
"Inspector.MessageError": true,
|
||
|
"Inspector.Message": true,
|
||
|
"Inspector.MethodType": true,
|
||
|
"Network.LoaderId": true,
|
||
|
"Network.Timestamp": true,
|
||
|
"Page.FrameId": true,
|
||
|
"Page.Frame": true,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// if the internal type locations change above, these will also need to change:
|
||
|
const (
|
||
|
domNodeIDRef = "NodeID"
|
||
|
domNodeRef = "*Node"
|
||
|
cssComputedStylePropertyRef = "*ComputedProperty"
|
||
|
)
|
||
|
|
||
|
// FixupDomains changes types in the domains, so that the generated code is
|
||
|
// more type specific, and easier to use.
|
||
|
//
|
||
|
// currently:
|
||
|
// - add 'Inspector.MethodType' type as a string enumeration of all the event/command names.
|
||
|
// - add 'Inspector.MessageError' type as a object with code (integer), and message (string).
|
||
|
// - add 'Inspector.Message' type as a object with id (integer), method (MethodType), params (interface{}), error (MessageError).
|
||
|
// - add 'Inspector.DetachReason' type and change event 'Inspector.detached''s parameter reason's type.
|
||
|
// - add 'Inspector.ErrorType' type.
|
||
|
// - change 'Runtime.Timestamp' to 'Network.Timestamp'.
|
||
|
// - change any object property or command/event parameter named 'timestamp'
|
||
|
// or has $ref to Network/Runtime.Timestamp to type 'Network.Timestamp'.
|
||
|
// - convert object properties and event/command parameters that are enums into independent types.
|
||
|
// - change '*.modifiers' parameters to type Input.Modifier.
|
||
|
// - add 'DOM.NodeType' type and convert "nodeType" parameters to it.
|
||
|
// - change Page.Frame.id/parentID to FrameID type.
|
||
|
// - add additional properties to 'Page.Frame' and 'DOM.Node' for use by higher level packages.
|
||
|
// - add special unmarshaler to NodeId, BackendNodeId, FrameId to handle values from older (v1.1) protocol versions. -- NOTE: this might need to be applied to more types, such as network.LoaderId
|
||
|
// - rename 'Input.GestureSourceType' -> 'Input.GestureType'.
|
||
|
// - rename CSS.CSS* types.
|
||
|
func FixupDomains(domains []*Domain) {
|
||
|
// method type
|
||
|
methodType := &Type{
|
||
|
ID: "MethodType",
|
||
|
Type: TypeString,
|
||
|
Description: "Chrome Debugging Protocol method type (ie, event and command names).",
|
||
|
EnumValueNameMap: make(map[string]string),
|
||
|
Extra: templates.ExtraMethodTypeDomainDecoder(),
|
||
|
}
|
||
|
|
||
|
// message error type
|
||
|
messageErrorType := &Type{
|
||
|
ID: "MessageError",
|
||
|
Type: TypeObject,
|
||
|
Description: "Message error type.",
|
||
|
Properties: []*Type{
|
||
|
&Type{
|
||
|
Name: "code",
|
||
|
Type: TypeInteger,
|
||
|
Description: "Error code.",
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "message",
|
||
|
Type: TypeString,
|
||
|
Description: "Error message.",
|
||
|
},
|
||
|
},
|
||
|
Extra: "// Error satisfies error interface.\nfunc (e *MessageError) Error() string {\nreturn fmt.Sprintf(\"%s (%d)\", e.Message, e.Code)\n}\n",
|
||
|
}
|
||
|
|
||
|
// message type
|
||
|
messageType := &Type{
|
||
|
ID: "Message",
|
||
|
Type: TypeObject,
|
||
|
Description: "Chrome Debugging Protocol message sent to/read over websocket connection.",
|
||
|
Properties: []*Type{
|
||
|
&Type{
|
||
|
Name: "id",
|
||
|
Type: TypeInteger,
|
||
|
Description: "Unique message identifier.",
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "method",
|
||
|
Ref: "Inspector.MethodType",
|
||
|
Description: "Event or command type.",
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "params",
|
||
|
Type: TypeAny,
|
||
|
Description: "Event or command parameters.",
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "result",
|
||
|
Type: TypeAny,
|
||
|
Description: "Command return values.",
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "error",
|
||
|
Ref: "MessageError",
|
||
|
Description: "Error message.",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// detach reason type
|
||
|
detachReasonType := &Type{
|
||
|
ID: "DetachReason",
|
||
|
Type: TypeString,
|
||
|
Enum: []string{"target_closed", "canceled_by_user", "replaced_with_devtools", "Render process gone."},
|
||
|
Description: "Detach reason.",
|
||
|
}
|
||
|
|
||
|
// cdp error types
|
||
|
errorValues := []string{"context done", "channel closed", "invalid result", "unknown result"}
|
||
|
errorValueNameMap := make(map[string]string)
|
||
|
for _, e := range errorValues {
|
||
|
errorValueNameMap[e] = "Err" + ForceCamel(e)
|
||
|
}
|
||
|
errorType := &Type{
|
||
|
ID: "ErrorType",
|
||
|
Type: TypeString,
|
||
|
Enum: errorValues,
|
||
|
EnumValueNameMap: errorValueNameMap,
|
||
|
Description: "Error type.",
|
||
|
Extra: templates.ExtraInternalTypes(),
|
||
|
}
|
||
|
|
||
|
// modifier type
|
||
|
modifierType := &Type{
|
||
|
ID: "Modifier",
|
||
|
Type: TypeInteger,
|
||
|
EnumBitMask: true,
|
||
|
Description: "Input key modifier type.",
|
||
|
Enum: []string{"None", "Alt", "Ctrl", "Meta", "Shift"},
|
||
|
Extra: "// ModifierCommand is an alias for ModifierMeta.\nconst ModifierCommand Modifier = ModifierMeta",
|
||
|
}
|
||
|
|
||
|
// node type type -- see: https://developer.mozilla.org/en/docs/Web/API/Node/nodeType
|
||
|
nodeTypeType := &Type{
|
||
|
ID: "NodeType",
|
||
|
Type: TypeInteger,
|
||
|
Description: "Node type.",
|
||
|
Enum: []string{
|
||
|
"Element", "Attribute", "Text", "CDATA", "EntityReference",
|
||
|
"Entity", "ProcessingInstruction", "Comment", "Document",
|
||
|
"DocumentType", "DocumentFragment", "Notation",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// process domains
|
||
|
for _, d := range domains {
|
||
|
switch d.Domain {
|
||
|
case DomainInspector:
|
||
|
// add Inspector types
|
||
|
d.Types = append(d.Types, messageErrorType, messageType, methodType, detachReasonType, errorType)
|
||
|
|
||
|
// find detached event's reason parameter and change type
|
||
|
for _, e := range d.Events {
|
||
|
if e.Name == "detached" {
|
||
|
for _, t := range e.Parameters {
|
||
|
if t.Name == "reason" {
|
||
|
t.Ref = "DetachReason"
|
||
|
t.Type = TypeEnum("")
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainCSS:
|
||
|
for _, t := range d.Types {
|
||
|
if t.ID == "CSSComputedStyleProperty" {
|
||
|
t.ID = "ComputedProperty"
|
||
|
} else {
|
||
|
t.ID = strings.TrimPrefix(t.ID, "CSS")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainInput:
|
||
|
// add Input types
|
||
|
d.Types = append(d.Types, modifierType)
|
||
|
for _, t := range d.Types {
|
||
|
if t.ID == "GestureSourceType" {
|
||
|
t.ID = "GestureType"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainDOM:
|
||
|
// add DOM types
|
||
|
d.Types = append(d.Types, nodeTypeType)
|
||
|
|
||
|
for _, t := range d.Types {
|
||
|
switch t.ID {
|
||
|
case "NodeId", "BackendNodeId":
|
||
|
t.Extra += templates.ExtraFixStringUnmarshaler(ForceCamel(t.ID), "ParseInt", ", 10, 64")
|
||
|
|
||
|
case "Node":
|
||
|
t.Properties = append(t.Properties,
|
||
|
&Type{
|
||
|
Name: "Parent",
|
||
|
Ref: domNodeRef,
|
||
|
Description: "Parent node.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
/*&Type{
|
||
|
Name: "Ready",
|
||
|
Ref: "chan struct{}",
|
||
|
Description: "Ready channel.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},*/
|
||
|
&Type{
|
||
|
Name: "Invalidated",
|
||
|
Ref: "chan struct{}",
|
||
|
Description: "Invalidated channel.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "State",
|
||
|
Ref: "NodeState",
|
||
|
Description: "Node state.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
/*&Type{
|
||
|
Name: "ComputedStyle",
|
||
|
Ref: "[]" + cssComputedStylePropertyRef,
|
||
|
Description: "Computed style.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "Valid",
|
||
|
Ref: "bool",
|
||
|
Description: "Valid state.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "Hidden",
|
||
|
Ref: "bool",
|
||
|
Description: "Hidden state.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},*/
|
||
|
&Type{
|
||
|
Name: "",
|
||
|
Ref: "sync.RWMutex",
|
||
|
Description: "Read write mutex.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
)
|
||
|
t.Extra += templates.ExtraNodeTemplate()
|
||
|
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainPage:
|
||
|
for _, t := range d.Types {
|
||
|
switch t.ID {
|
||
|
case "FrameId":
|
||
|
t.Extra += templates.ExtraFixStringUnmarshaler(ForceCamel(t.ID), "", "")
|
||
|
|
||
|
case "Frame":
|
||
|
t.Properties = append(t.Properties,
|
||
|
&Type{
|
||
|
Name: "State",
|
||
|
Ref: "FrameState",
|
||
|
Description: "Frame state.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "Root",
|
||
|
Ref: domNodeRef,
|
||
|
Description: "Frame document root.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "Nodes",
|
||
|
Ref: "map[" + domNodeIDRef + "]" + domNodeRef,
|
||
|
Description: "Frame nodes.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
&Type{
|
||
|
Name: "",
|
||
|
Ref: "sync.RWMutex",
|
||
|
Description: "Read write mutex.",
|
||
|
NoResolve: true,
|
||
|
NoExpose: true,
|
||
|
},
|
||
|
)
|
||
|
t.Extra += templates.ExtraFrameTemplate()
|
||
|
|
||
|
// convert Frame.id/parentId to $ref of FrameID
|
||
|
for _, p := range t.Properties {
|
||
|
if p.Name == "id" || p.Name == "parentId" {
|
||
|
p.Ref = "FrameId"
|
||
|
p.Type = TypeEnum("")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainNetwork:
|
||
|
for _, t := range d.Types {
|
||
|
// change Timestamp to TypeTimestamp and add extra unmarshaling template
|
||
|
if t.ID == "Timestamp" {
|
||
|
t.Type = TypeTimestamp
|
||
|
t.Extra += templates.ExtraTimestampTemplate(t, d)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case DomainRuntime:
|
||
|
var types []*Type
|
||
|
for _, t := range d.Types {
|
||
|
if t.ID == "Timestamp" {
|
||
|
continue
|
||
|
}
|
||
|
types = append(types, t)
|
||
|
}
|
||
|
d.Types = types
|
||
|
}
|
||
|
|
||
|
for _, t := range d.Types {
|
||
|
// convert object properties
|
||
|
if t.Properties != nil {
|
||
|
t.Properties = convertObjectProperties(t.Properties, d, t.ID)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// process events and commands
|
||
|
processTypesWithParameters(methodType, d, d.Events, EventMethodPrefix, EventMethodSuffix)
|
||
|
processTypesWithParameters(methodType, d, d.Commands, CommandMethodPrefix, CommandMethodSuffix)
|
||
|
|
||
|
// fix input enums
|
||
|
if d.Domain == DomainInput {
|
||
|
for _, t := range d.Types {
|
||
|
if t.Enum != nil && t.ID != "Modifier" {
|
||
|
t.EnumValueNameMap = make(map[string]string)
|
||
|
for _, v := range t.Enum {
|
||
|
prefix := ""
|
||
|
switch t.ID {
|
||
|
case "GestureType":
|
||
|
prefix = "Gesture"
|
||
|
case "ButtonType":
|
||
|
prefix = "Button"
|
||
|
}
|
||
|
n := prefix + ForceCamel(v)
|
||
|
if t.ID == "KeyType" {
|
||
|
n = "Key" + strings.Replace(n, "Key", "", -1)
|
||
|
}
|
||
|
t.EnumValueNameMap[v] = strings.Replace(n, "Cancell", "Cancel", -1)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// processTypesWithParameters adds the types to t's enum values, setting the
|
||
|
// enum value map for m. Also, converts the Parameters and Returns properties.
|
||
|
func processTypesWithParameters(m *Type, d *Domain, types []*Type, prefix, suffix string) {
|
||
|
for _, t := range types {
|
||
|
n := t.ProtoName(d)
|
||
|
m.Enum = append(m.Enum, n)
|
||
|
m.EnumValueNameMap[n] = t.TypeName(prefix+d.String(), suffix)
|
||
|
|
||
|
t.Parameters = convertObjectProperties(t.Parameters, d, t.Name)
|
||
|
if t.Returns != nil {
|
||
|
t.Returns = convertObjectProperties(t.Returns, d, t.Name)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// convertObjectProperties converts object properties.
|
||
|
func convertObjectProperties(params []*Type, d *Domain, name string) []*Type {
|
||
|
r := make([]*Type, 0)
|
||
|
for _, p := range params {
|
||
|
switch {
|
||
|
case p.Items != nil:
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Type: TypeArray,
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
Items: convertObjectProperties([]*Type{p.Items}, d, name+"."+p.Name)[0],
|
||
|
})
|
||
|
|
||
|
case p.Enum != nil:
|
||
|
r = append(r, fixupEnumParameter(name, p, d))
|
||
|
|
||
|
case (p.Name == "timestamp" || p.Ref == "Network.Timestamp" || p.Ref == "Timestamp") && d.Domain != DomainInput:
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: "Network.Timestamp",
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
case p.Name == "modifiers":
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: "Modifier",
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
case p.Name == "nodeType":
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: "NodeType",
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
case p.Ref == "GestureSourceType":
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: "GestureType",
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
case p.Ref == "CSSComputedStyleProperty":
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: "ComputedProperty",
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
case strings.HasPrefix(p.Ref, "CSS"):
|
||
|
r = append(r, &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: strings.TrimPrefix(p.Ref, "CSS"),
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
})
|
||
|
|
||
|
default:
|
||
|
r = append(r, p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return r
|
||
|
}
|
||
|
|
||
|
// addEnumValues adds orig.Enum values to type named n's Enum values in domain.
|
||
|
func addEnumValues(d *Domain, n string, p *Type) {
|
||
|
// find type
|
||
|
var typ *Type
|
||
|
for _, t := range d.Types {
|
||
|
if t.ID == n {
|
||
|
typ = t
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if typ == nil {
|
||
|
typ = &Type{
|
||
|
ID: n,
|
||
|
Type: TypeString,
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
}
|
||
|
d.Types = append(d.Types, typ)
|
||
|
}
|
||
|
|
||
|
// combine typ.Enum and vals
|
||
|
v := make(map[string]bool)
|
||
|
all := append(typ.Enum, p.Enum...)
|
||
|
for _, z := range all {
|
||
|
v[z] = false
|
||
|
}
|
||
|
|
||
|
i := 0
|
||
|
typ.Enum = make([]string, len(v))
|
||
|
for _, z := range all {
|
||
|
if !v[z] {
|
||
|
typ.Enum[i] = z
|
||
|
i++
|
||
|
}
|
||
|
v[z] = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// enumRefMap is the fully qualified parameter name to ref.
|
||
|
var enumRefMap = map[string]string{
|
||
|
"CSS.forcePseudoState.forcedPseudoClasses": "PseudoClass",
|
||
|
"CSS.Media.source": "MediaSource",
|
||
|
"Debugger.setPauseOnExceptions.state": "ExceptionsState",
|
||
|
"Emulation.ScreenOrientation.type": "OrientationType",
|
||
|
"Emulation.setTouchEmulationEnabled.configuration": "EnabledConfiguration",
|
||
|
"Input.dispatchKeyEvent.type": "KeyType",
|
||
|
"Input.dispatchMouseEvent.button": "ButtonType",
|
||
|
"Input.dispatchMouseEvent.type": "MouseType",
|
||
|
"Input.dispatchTouchEvent.type": "TouchType",
|
||
|
"Input.emulateTouchFromMouseEvent.button": "ButtonType",
|
||
|
"Input.emulateTouchFromMouseEvent.type": "MouseType",
|
||
|
"Input.TouchPoint.state": "TouchState",
|
||
|
"Log.LogEntry.level": "Level",
|
||
|
"Log.LogEntry.source": "Source",
|
||
|
"Log.ViolationSetting.name": "Violation",
|
||
|
"Network.Request.mixedContentType": "MixedContentType",
|
||
|
"Network.Request.referrerPolicy": "ReferrerPolicy",
|
||
|
"Page.startScreencast.format": "ScreencastFormat",
|
||
|
"Runtime.consoleAPICalled.type": "APIType",
|
||
|
"Runtime.ObjectPreview.subtype": "Subtype",
|
||
|
"Runtime.ObjectPreview.type": "Type",
|
||
|
"Runtime.PropertyPreview.subtype": "Subtype",
|
||
|
"Runtime.PropertyPreview.type": "Type",
|
||
|
"Runtime.RemoteObject.subtype": "Subtype",
|
||
|
"Runtime.RemoteObject.type": "Type",
|
||
|
"Tracing.start.transferMode": "TransferMode",
|
||
|
"Tracing.TraceConfig.recordMode": "RecordMode",
|
||
|
}
|
||
|
|
||
|
// fixupEnumParameter takes an enum parameter, adds it to the domain and
|
||
|
// returns a type suitable for use in place of the type.
|
||
|
func fixupEnumParameter(typ string, p *Type, d *Domain) *Type {
|
||
|
fqname := strings.TrimSuffix(fmt.Sprintf("%s.%s.%s", d.Domain, typ, p.Name), ".")
|
||
|
ref := ForceCamel(typ + "." + p.Name)
|
||
|
if n, ok := enumRefMap[fqname]; ok {
|
||
|
ref = n
|
||
|
}
|
||
|
|
||
|
// add enum values to type name
|
||
|
addEnumValues(d, ref, p)
|
||
|
|
||
|
return &Type{
|
||
|
Name: p.Name,
|
||
|
Ref: ref,
|
||
|
Description: p.Description,
|
||
|
Optional: p.Optional,
|
||
|
}
|
||
|
}
|