From 2bc9c6ad0bc50ae5e038d7eeff6c05347a346d97 Mon Sep 17 00:00:00 2001 From: Kenneth Shaw Date: Thu, 28 Sep 2017 15:41:05 +0700 Subject: [PATCH] Updating to latest protocol.json --- cdp/cdp.go | 2 +- cdp/css/types.go | 18 +++++++++--------- cdp/debugger/debugger.go | 4 ++-- cdp/debugger/types.go | 2 +- cdp/dom/dom.go | 16 ++++++++-------- cdp/dom/types.go | 4 ++-- cdp/domdebugger/types.go | 2 +- cdp/domsnapshot/domsnapshot.go | 3 +-- cdp/domstorage/types.go | 4 ++-- cdp/heapprofiler/types.go | 2 +- cdp/indexeddb/types.go | 2 +- cdp/input/input.go | 8 ++++---- cdp/network/easyjson.go | 8 ++++---- cdp/network/types.go | 8 ++++---- cdp/overlay/overlay.go | 2 +- cdp/profiler/types.go | 2 +- cdp/runtime/events.go | 2 +- cdp/runtime/types.go | 2 +- cmd/chromedp-gen/protocol.json | 8 ++++---- cmd/chromedp-gen/templates/util.go | 26 ++++++++++++++++++++++++-- 20 files changed, 73 insertions(+), 52 deletions(-) diff --git a/cdp/cdp.go b/cdp/cdp.go index 22ff2e2..914cbe4 100644 --- a/cdp/cdp.go +++ b/cdp/cdp.go @@ -1828,7 +1828,7 @@ func (t *ShadowRootType) UnmarshalJSON(buf []byte) error { return easyjson.Unmarshal(buf, t) } -// Node dOM interaction is implemented in terms of mirror objects that +// Node DOM interaction is implemented in terms of mirror objects that // represent the actual DOM nodes. DOMNode is a base node mirror type. type Node struct { NodeID NodeID `json:"nodeId"` // 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. diff --git a/cdp/css/types.go b/cdp/css/types.go index 5ad5042..9fbf8bd 100644 --- a/cdp/css/types.go +++ b/cdp/css/types.go @@ -71,7 +71,7 @@ func (t *StyleSheetOrigin) UnmarshalJSON(buf []byte) error { return easyjson.Unmarshal(buf, t) } -// PseudoElementMatches cSS rule collection for a single pseudo style. +// PseudoElementMatches CSS rule collection for a single pseudo style. type PseudoElementMatches struct { PseudoType cdp.PseudoType `json:"pseudoType"` // Pseudo element type. Matches []*RuleMatch `json:"matches"` // Matches of CSS rules applicable to the pseudo style. @@ -102,7 +102,7 @@ type SelectorList struct { Text string `json:"text"` // Rule selector text. } -// StyleSheetHeader cSS stylesheet metainformation. +// StyleSheetHeader CSS stylesheet metainformation. type StyleSheetHeader struct { StyleSheetID StyleSheetID `json:"styleSheetId"` // The stylesheet identifier. FrameID cdp.FrameID `json:"frameId"` // Owner frame identifier. @@ -119,7 +119,7 @@ type StyleSheetHeader struct { Length float64 `json:"length"` // Size of the content (in characters). } -// Rule cSS rule representation. +// Rule CSS rule representation. type Rule struct { StyleSheetID StyleSheetID `json:"styleSheetId,omitempty"` // The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from. SelectorList *SelectorList `json:"selectorList"` // Rule selector data. @@ -128,7 +128,7 @@ type Rule struct { Media []*Media `json:"media,omitempty"` // Media list array (for rules involving media queries). The array enumerates media queries starting with the innermost one, going outwards. } -// RuleUsage cSS coverage information. +// RuleUsage CSS coverage information. type RuleUsage struct { StyleSheetID StyleSheetID `json:"styleSheetId"` // The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from. StartOffset float64 `json:"startOffset"` // Offset of the start of the rule (including selector) from the beginning of the stylesheet. @@ -157,7 +157,7 @@ type ComputedProperty struct { Value string `json:"value"` // Computed style property value. } -// Style cSS style representation. +// Style CSS style representation. type Style struct { StyleSheetID StyleSheetID `json:"styleSheetId,omitempty"` // The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from. CSSProperties []*Property `json:"cssProperties"` // CSS properties in the style. @@ -166,7 +166,7 @@ type Style struct { Range *SourceRange `json:"range,omitempty"` // Style declaration range in the enclosing stylesheet (if available). } -// Property cSS property declaration data. +// Property CSS property declaration data. type Property struct { Name string `json:"name"` // The property name. Value string `json:"value"` // The property value. @@ -178,7 +178,7 @@ type Property struct { Range *SourceRange `json:"range,omitempty"` // The entire property range in the enclosing style declaration (if available). } -// Media cSS media rule descriptor. +// Media CSS media rule descriptor. type Media struct { Text string `json:"text"` // Media query text. Source MediaSource `json:"source"` // Source of the media query: "mediaRule" if specified by a @media rule, "importRule" if specified by an @import rule, "linkedSheet" if specified by a "media" attribute in a linked stylesheet's LINK tag, "inlineSheet" if specified by a "media" attribute in an inline stylesheet's STYLE tag. @@ -211,13 +211,13 @@ type PlatformFontUsage struct { GlyphCount float64 `json:"glyphCount"` // Amount of glyphs that were rendered with this font. } -// KeyframesRule cSS keyframes rule representation. +// KeyframesRule CSS keyframes rule representation. type KeyframesRule struct { AnimationName *Value `json:"animationName"` // Animation name. Keyframes []*KeyframeRule `json:"keyframes"` // List of keyframes. } -// KeyframeRule cSS keyframe rule representation. +// KeyframeRule CSS keyframe rule representation. type KeyframeRule struct { StyleSheetID StyleSheetID `json:"styleSheetId,omitempty"` // The css style sheet identifier (absent for user agent stylesheet and user-specified stylesheet rules) this rule came from. Origin StyleSheetOrigin `json:"origin"` // Parent stylesheet's origin. diff --git a/cdp/debugger/debugger.go b/cdp/debugger/debugger.go index 465dfc3..339f47a 100644 --- a/cdp/debugger/debugger.go +++ b/cdp/debugger/debugger.go @@ -121,7 +121,7 @@ func SetBreakpointByURL(lineNumber int64) *SetBreakpointByURLParams { } } -// WithURL uRL of the resources to set breakpoint on. +// WithURL URL of the resources to set breakpoint on. func (p SetBreakpointByURLParams) WithURL(url string) *SetBreakpointByURLParams { p.URL = url return &p @@ -484,7 +484,7 @@ func SetScriptSource(scriptID runtime.ScriptID, scriptSource string) *SetScriptS } } -// WithDryRun If true the change will not actually be applied. Dry run may be +// WithDryRun if true the change will not actually be applied. Dry run may be // used to get result description without actually modifying the code. func (p SetScriptSourceParams) WithDryRun(dryRun bool) *SetScriptSourceParams { p.DryRun = dryRun diff --git a/cdp/debugger/types.go b/cdp/debugger/types.go index 19bc73e..d4e5613 100644 --- a/cdp/debugger/types.go +++ b/cdp/debugger/types.go @@ -40,7 +40,7 @@ type ScriptPosition struct { ColumnNumber int64 `json:"columnNumber"` } -// CallFrame javaScript call frame. Array of call frames form the call stack. +// CallFrame JavaScript call frame. Array of call frames form the call stack. type CallFrame struct { CallFrameID CallFrameID `json:"callFrameId"` // Call frame identifier. This identifier is only valid while the virtual machine is paused. FunctionName string `json:"functionName"` // Name of the JavaScript function called on this call frame. diff --git a/cdp/dom/dom.go b/cdp/dom/dom.go index 6f9e373..9ddc4c6 100644 --- a/cdp/dom/dom.go +++ b/cdp/dom/dom.go @@ -512,7 +512,7 @@ func (p GetOuterHTMLParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) * return &p } -// WithObjectID javaScript object id of the node wrapper. +// WithObjectID JavaScript object id of the node wrapper. func (p GetOuterHTMLParams) WithObjectID(objectID runtime.RemoteObjectID) *GetOuterHTMLParams { p.ObjectID = objectID return &p @@ -613,7 +613,7 @@ func (p *PerformSearchParams) Do(ctxt context.Context, h cdp.Handler) (searchID } // GetSearchResultsParams returns search results from given fromIndex to -// given toIndex from the sarch with the given identifier. +// given toIndex from the search with the given identifier. type GetSearchResultsParams struct { SearchID string `json:"searchId"` // Unique search session identifier. FromIndex int64 `json:"fromIndex"` // Start index of the search result to be returned. @@ -621,7 +621,7 @@ type GetSearchResultsParams struct { } // GetSearchResults returns search results from given fromIndex to given -// toIndex from the sarch with the given identifier. +// toIndex from the search with the given identifier. // // parameters: // searchID - Unique search session identifier. @@ -836,7 +836,7 @@ func ResolveNode() *ResolveNodeParams { return &ResolveNodeParams{} } -// WithNodeID id of the node to resolve. +// WithNodeID ID of the node to resolve. func (p ResolveNodeParams) WithNodeID(nodeID cdp.NodeID) *ResolveNodeParams { p.NodeID = nodeID return &p @@ -1078,7 +1078,7 @@ func (p FocusParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) *FocusPa return &p } -// WithObjectID javaScript object id of the node wrapper. +// WithObjectID JavaScript object id of the node wrapper. func (p FocusParams) WithObjectID(objectID runtime.RemoteObjectID) *FocusParams { p.ObjectID = objectID return &p @@ -1120,7 +1120,7 @@ func (p SetFileInputFilesParams) WithBackendNodeID(backendNodeID cdp.BackendNode return &p } -// WithObjectID javaScript object id of the node wrapper. +// WithObjectID JavaScript object id of the node wrapper. func (p SetFileInputFilesParams) WithObjectID(objectID runtime.RemoteObjectID) *SetFileInputFilesParams { p.ObjectID = objectID return &p @@ -1158,7 +1158,7 @@ func (p GetBoxModelParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) *G return &p } -// WithObjectID javaScript object id of the node wrapper. +// WithObjectID JavaScript object id of the node wrapper. func (p GetBoxModelParams) WithObjectID(objectID runtime.RemoteObjectID) *GetBoxModelParams { p.ObjectID = objectID return &p @@ -1300,7 +1300,7 @@ func (p DescribeNodeParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) * return &p } -// WithObjectID javaScript object id of the node wrapper. +// WithObjectID JavaScript object id of the node wrapper. func (p DescribeNodeParams) WithObjectID(objectID runtime.RemoteObjectID) *DescribeNodeParams { p.ObjectID = objectID return &p diff --git a/cdp/dom/types.go b/cdp/dom/types.go index 2c05a41..02a64a9 100644 --- a/cdp/dom/types.go +++ b/cdp/dom/types.go @@ -19,14 +19,14 @@ type BoxModel struct { ShapeOutside *ShapeOutsideInfo `json:"shapeOutside,omitempty"` // Shape outside coordinates } -// ShapeOutsideInfo cSS Shape Outside details. +// ShapeOutsideInfo CSS Shape Outside details. type ShapeOutsideInfo struct { Bounds Quad `json:"bounds"` // Shape bounds Shape []easyjson.RawMessage `json:"shape"` // Shape coordinate details MarginShape []easyjson.RawMessage `json:"marginShape"` // Margin shape bounds } -// Rect rectangle. +// Rect Rectangle. type Rect struct { X float64 `json:"x"` // X coordinate Y float64 `json:"y"` // Y coordinate diff --git a/cdp/domdebugger/types.go b/cdp/domdebugger/types.go index 97ffef6..3881268 100644 --- a/cdp/domdebugger/types.go +++ b/cdp/domdebugger/types.go @@ -12,7 +12,7 @@ import ( "github.com/mailru/easyjson/jwriter" ) -// DOMBreakpointType dOM breakpoint type. +// DOMBreakpointType DOM breakpoint type. type DOMBreakpointType string // String returns the DOMBreakpointType as string value. diff --git a/cdp/domsnapshot/domsnapshot.go b/cdp/domsnapshot/domsnapshot.go index e3d9be3..7e37e5f 100644 --- a/cdp/domsnapshot/domsnapshot.go +++ b/cdp/domsnapshot/domsnapshot.go @@ -19,7 +19,7 @@ import ( // of the root node (including iframes, template contents, and imported // documents) in a flattened array, as well as layout and white-listed computed // style information for the nodes. Shadow DOM in the returned DOM tree is -// flattened. . +// flattened. type GetSnapshotParams struct { ComputedStyleWhitelist []string `json:"computedStyleWhitelist"` // Whitelist of computed styles to return. } @@ -28,7 +28,6 @@ type GetSnapshotParams struct { // the root node (including iframes, template contents, and imported documents) // in a flattened array, as well as layout and white-listed computed style // information for the nodes. Shadow DOM in the returned DOM tree is flattened. -// . // // parameters: // computedStyleWhitelist - Whitelist of computed styles to return. diff --git a/cdp/domstorage/types.go b/cdp/domstorage/types.go index 1ce6a9d..0e17d1f 100644 --- a/cdp/domstorage/types.go +++ b/cdp/domstorage/types.go @@ -2,11 +2,11 @@ package domstorage // Code generated by chromedp-gen. DO NOT EDIT. -// StorageID dOM Storage identifier. +// StorageID DOM Storage identifier. type StorageID struct { SecurityOrigin string `json:"securityOrigin"` // Security origin for the storage. IsLocalStorage bool `json:"isLocalStorage"` // Whether the storage is local storage (not session storage). } -// Item dOM Storage item. +// Item DOM Storage item. type Item []string diff --git a/cdp/heapprofiler/types.go b/cdp/heapprofiler/types.go index f029b63..2471d85 100644 --- a/cdp/heapprofiler/types.go +++ b/cdp/heapprofiler/types.go @@ -20,7 +20,7 @@ type SamplingHeapProfileNode struct { Children []*SamplingHeapProfileNode `json:"children"` // Child nodes. } -// SamplingHeapProfile profile. +// SamplingHeapProfile Profile. type SamplingHeapProfile struct { Head *SamplingHeapProfileNode `json:"head"` } diff --git a/cdp/indexeddb/types.go b/cdp/indexeddb/types.go index df2089a..0a14b12 100644 --- a/cdp/indexeddb/types.go +++ b/cdp/indexeddb/types.go @@ -34,7 +34,7 @@ type ObjectStoreIndex struct { MultiEntry bool `json:"multiEntry"` // If true, index allows multiple entries for a key. } -// Key key. +// Key Key. type Key struct { Type KeyType `json:"type"` // Key type. Number float64 `json:"number,omitempty"` // Number value. diff --git a/cdp/input/input.go b/cdp/input/input.go index 8755b68..d82ae5f 100644 --- a/cdp/input/input.go +++ b/cdp/input/input.go @@ -201,13 +201,13 @@ func (p DispatchMouseEventParams) WithClickCount(clickCount int64) *DispatchMous return &p } -// WithDeltaX x delta in CSS pixels for mouse wheel event (default: 0). +// WithDeltaX X delta in CSS pixels for mouse wheel event (default: 0). func (p DispatchMouseEventParams) WithDeltaX(deltaX float64) *DispatchMouseEventParams { p.DeltaX = deltaX return &p } -// WithDeltaY y delta in CSS pixels for mouse wheel event (default: 0). +// WithDeltaY Y delta in CSS pixels for mouse wheel event (default: 0). func (p DispatchMouseEventParams) WithDeltaY(deltaY float64) *DispatchMouseEventParams { p.DeltaY = deltaY return &p @@ -291,13 +291,13 @@ func EmulateTouchFromMouseEvent(typeVal MouseType, x int64, y int64, timestamp * } } -// WithDeltaX x delta in DIP for mouse wheel event (default: 0). +// WithDeltaX X delta in DIP for mouse wheel event (default: 0). func (p EmulateTouchFromMouseEventParams) WithDeltaX(deltaX float64) *EmulateTouchFromMouseEventParams { p.DeltaX = deltaX return &p } -// WithDeltaY y delta in DIP for mouse wheel event (default: 0). +// WithDeltaY Y delta in DIP for mouse wheel event (default: 0). func (p EmulateTouchFromMouseEventParams) WithDeltaY(deltaY float64) *EmulateTouchFromMouseEventParams { p.DeltaY = deltaY return &p diff --git a/cdp/network/easyjson.go b/cdp/network/easyjson.go index 4c20421..3f984b6 100644 --- a/cdp/network/easyjson.go +++ b/cdp/network/easyjson.go @@ -40,7 +40,7 @@ func easyjsonC5a4559bDecodeGithubComKnqChromedpCdpNetwork(in *jlexer.Lexer, out } switch key { case "status": - out.Status = float64(in.Float64()) + out.Status = int64(in.Int64()) case "statusText": out.StatusText = string(in.String()) case "headers": @@ -118,7 +118,7 @@ func easyjsonC5a4559bEncodeGithubComKnqChromedpCdpNetwork(out *jwriter.Writer, i } first = false out.RawString("\"status\":") - out.Float64(float64(in.Status)) + out.Int64(int64(in.Status)) if !first { out.RawByte(',') } @@ -1767,7 +1767,7 @@ func easyjsonC5a4559bDecodeGithubComKnqChromedpCdpNetwork15(in *jlexer.Lexer, ou case "url": out.URL = string(in.String()) case "status": - out.Status = float64(in.Float64()) + out.Status = int64(in.Int64()) case "statusText": out.StatusText = string(in.String()) case "headers": @@ -1891,7 +1891,7 @@ func easyjsonC5a4559bEncodeGithubComKnqChromedpCdpNetwork15(out *jwriter.Writer, } first = false out.RawString("\"status\":") - out.Float64(float64(in.Status)) + out.Int64(int64(in.Status)) if !first { out.RawByte(',') } diff --git a/cdp/network/types.go b/cdp/network/types.go index 4877a83..a25a007 100644 --- a/cdp/network/types.go +++ b/cdp/network/types.go @@ -283,7 +283,7 @@ func (t *ResourcePriority) UnmarshalJSON(buf []byte) error { return easyjson.Unmarshal(buf, t) } -// Request hTTP request data. +// Request HTTP request data. type Request struct { URL string `json:"url"` // Request URL. Method string `json:"method"` // HTTP request method. @@ -378,10 +378,10 @@ func (t *BlockedReason) UnmarshalJSON(buf []byte) error { return easyjson.Unmarshal(buf, t) } -// Response hTTP response data. +// Response HTTP response data. type Response struct { URL string `json:"url"` // Response URL. This URL can be different from CachedResource.url in case of redirect. - Status float64 `json:"status"` // HTTP response status code. + Status int64 `json:"status"` // HTTP response status code. StatusText string `json:"statusText"` // HTTP response status text. Headers Headers `json:"headers"` // HTTP response headers. HeadersText string `json:"headersText,omitempty"` // HTTP response headers text. @@ -408,7 +408,7 @@ type WebSocketRequest struct { // WebSocketResponse webSocket response data. type WebSocketResponse struct { - Status float64 `json:"status"` // HTTP response status code. + Status int64 `json:"status"` // HTTP response status code. StatusText string `json:"statusText"` // HTTP response status text. Headers Headers `json:"headers"` // HTTP response headers. HeadersText string `json:"headersText,omitempty"` // HTTP response headers text. diff --git a/cdp/overlay/overlay.go b/cdp/overlay/overlay.go index 0f6939a..d67fc60 100644 --- a/cdp/overlay/overlay.go +++ b/cdp/overlay/overlay.go @@ -349,7 +349,7 @@ func (p HighlightNodeParams) WithBackendNodeID(backendNodeID cdp.BackendNodeID) return &p } -// WithObjectID javaScript object id of the node to be highlighted. +// WithObjectID JavaScript object id of the node to be highlighted. func (p HighlightNodeParams) WithObjectID(objectID runtime.RemoteObjectID) *HighlightNodeParams { p.ObjectID = objectID return &p diff --git a/cdp/profiler/types.go b/cdp/profiler/types.go index 4021191..ec1a989 100644 --- a/cdp/profiler/types.go +++ b/cdp/profiler/types.go @@ -15,7 +15,7 @@ type ProfileNode struct { PositionTicks []*PositionTickInfo `json:"positionTicks,omitempty"` // An array of source position ticks. } -// Profile profile. +// Profile Profile. type Profile struct { Nodes []*ProfileNode `json:"nodes"` // The list of profile nodes. First item is the root node. StartTime float64 `json:"startTime"` // Profiling start timestamp in microseconds. diff --git a/cdp/runtime/events.go b/cdp/runtime/events.go index e938ae4..a7fc627 100644 --- a/cdp/runtime/events.go +++ b/cdp/runtime/events.go @@ -30,7 +30,7 @@ type EventExceptionThrown struct { // EventExceptionRevoked issued when unhandled exception was revoked. type EventExceptionRevoked struct { Reason string `json:"reason"` // Reason describing why exception was revoked. - ExceptionID int64 `json:"exceptionId"` // The id of revoked exception, as reported in exceptionUnhandled. + ExceptionID int64 `json:"exceptionId"` // The id of revoked exception, as reported in exceptionThrown. } // EventConsoleAPICalled issued when console API was called. diff --git a/cdp/runtime/types.go b/cdp/runtime/types.go index 073db81..b03b3b5 100644 --- a/cdp/runtime/types.go +++ b/cdp/runtime/types.go @@ -154,7 +154,7 @@ type CallArgument struct { ObjectID RemoteObjectID `json:"objectId,omitempty"` // Remote object handle. } -// ExecutionContextID id of an execution context. +// ExecutionContextID ID of an execution context. type ExecutionContextID int64 // Int64 returns the ExecutionContextID as int64 value. diff --git a/cmd/chromedp-gen/protocol.json b/cmd/chromedp-gen/protocol.json index d090a43..4602e2c 100644 --- a/cmd/chromedp-gen/protocol.json +++ b/cmd/chromedp-gen/protocol.json @@ -3001,7 +3001,7 @@ }, { "name": "status", - "type": "number", + "type": "integer", "description": "HTTP response status code." }, { @@ -3125,7 +3125,7 @@ "properties": [ { "name": "status", - "type": "number", + "type": "integer", "description": "HTTP response status code." }, { @@ -6183,7 +6183,7 @@ "description": "Ids of the search result nodes." } ], - "description": "Returns search results from given fromIndex to given toIndex from the sarch with the given identifier.", + "description": "Returns search results from given fromIndex to given toIndex from the search with the given identifier.", "experimental": true }, { @@ -12801,7 +12801,7 @@ { "name": "exceptionId", "type": "integer", - "description": "The id of revoked exception, as reported in exceptionUnhandled." + "description": "The id of revoked exception, as reported in exceptionThrown." } ] }, diff --git a/cmd/chromedp-gen/templates/util.go b/cmd/chromedp-gen/templates/util.go index 0c85412..f0fb4be 100644 --- a/cmd/chromedp-gen/templates/util.go +++ b/cmd/chromedp-gen/templates/util.go @@ -4,8 +4,10 @@ package templates import ( "strings" + "unicode" "github.com/knq/chromedp/cmd/chromedp-gen/internal" + "github.com/knq/snaker" ) const ( @@ -13,14 +15,34 @@ const ( commentPrefix = `// ` ) +var toUpper = map[string]bool{ + "DOM": true, + "X": true, + "Y": true, +} + +var keep = map[string]bool{ + "JavaScript": true, +} + // formatComment formats a comment. func formatComment(s, chop, newstr string) string { s = strings.TrimPrefix(s, chop) - s = internal.CodeRE.ReplaceAllString(s, "") + s = strings.TrimSpace(internal.CodeRE.ReplaceAllString(s, "")) l := len(s) if newstr != "" && l > 0 { - s = strings.ToLower(s[:1]) + s[1:] + if i := strings.IndexFunc(s, unicode.IsSpace); i != -1 { + firstWord, remaining := s[:i], s[i:] + if snaker.IsInitialism(firstWord) || toUpper[firstWord] { + s = strings.ToUpper(firstWord) + } else if keep[firstWord] { + s = firstWord + } else { + s = strings.ToLower(firstWord[:1]) + firstWord[1:] + } + s += remaining + } } s = newstr + strings.TrimSuffix(s, ".") if l < 1 {