srcset.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package sanitizer
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. )
  9. type imageCandidate struct {
  10. ImageURL string
  11. Descriptor string
  12. }
  13. type imageCandidates []*imageCandidate
  14. func (c imageCandidates) String() string {
  15. htmlCandidates := make([]string, 0, len(c))
  16. for _, imageCandidate := range c {
  17. var htmlCandidate string
  18. if imageCandidate.Descriptor != "" {
  19. htmlCandidate = imageCandidate.ImageURL + " " + imageCandidate.Descriptor
  20. } else {
  21. htmlCandidate = imageCandidate.ImageURL
  22. }
  23. htmlCandidates = append(htmlCandidates, htmlCandidate)
  24. }
  25. return strings.Join(htmlCandidates, ", ")
  26. }
  27. // ParseSrcSetAttribute returns the list of image candidates from the set.
  28. // https://html.spec.whatwg.org/#parse-a-srcset-attribute
  29. func ParseSrcSetAttribute(attributeValue string) (imageCandidates imageCandidates) {
  30. for _, unparsedCandidate := range strings.Split(attributeValue, ", ") {
  31. if candidate, err := parseImageCandidate(unparsedCandidate); err == nil {
  32. imageCandidates = append(imageCandidates, candidate)
  33. }
  34. }
  35. return imageCandidates
  36. }
  37. func parseImageCandidate(input string) (*imageCandidate, error) {
  38. parts := strings.Split(strings.TrimSpace(input), " ")
  39. nbParts := len(parts)
  40. switch nbParts {
  41. case 1:
  42. return &imageCandidate{ImageURL: parts[0]}, nil
  43. case 2:
  44. if !isValidWidthOrDensityDescriptor(parts[1]) {
  45. return nil, fmt.Errorf(`srcset: invalid descriptor`)
  46. }
  47. return &imageCandidate{ImageURL: parts[0], Descriptor: parts[1]}, nil
  48. default:
  49. return nil, fmt.Errorf(`srcset: invalid number of descriptors`)
  50. }
  51. }
  52. func isValidWidthOrDensityDescriptor(value string) bool {
  53. if value == "" {
  54. return false
  55. }
  56. lastChar := value[len(value)-1:]
  57. if lastChar != "w" && lastChar != "x" {
  58. return false
  59. }
  60. _, err := strconv.ParseFloat(value[0:len(value)-1], 32)
  61. return err == nil
  62. }