8000 Prefer href to xlink href by jfhenon · Pull Request #1059 · SVG-Edit/svgedit · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Prefer href to xlink href #1059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 9, 2025
Merged

Prefer href to xlink href #1059

merged 7 commits into from
Jun 9, 2025

Conversation

jfhenon
Copy link
Collaborator
@jfhenon jfhenon commented Jun 9, 2025

PR description

Summary
This pull request replaces all remaining uses of xlink:href with href in accordance with modern SVG standards. The xlink:href attribute has been deprecated and is no longer necessary in SVG 2, so this change improves compatibility and future-proofs the codebase.

Details
Refactored all occurrences of xlink:href to use href instead.
Updated relevant code, documentation, and test cases where applicable.
Ensured backward compatibility with browsers that support only href.
Testing
Manually tested the UI to verify that SVG elements referencing other elements (e.g., , ) still work as expected.
Ran automated tests to ensure no regressions were introduced.

Summary by Sourcery

Replace deprecated xlink:href attributes with modern href across the codebase to comply with SVG2 standards and maintain backward compatibility.

Enhancements:

  • Prefer href over xlink:href in core utilities with fallback to xlink for legacy support
  • Include href in the SVG sanitizer whitelist and update removal logic and warnings
  • Update Paint and svg-exec modules to use href for gradients and images
  • Adjust code formatting and iteration style where related changes were applied

Build:

  • Bump @svgedit/svgcanvas, i18next, Babel, Cypress, Rollup and other dependencies to latest patch versions

Tests:

  • Update unit and end-to-end tests to expect href attributes instead of xlink:href

@jfhenon jfhenon requested a review from Copilot June 9, 2025 08:22
Copy link
sourcery-ai bot commented Jun 9, 2025

Reviewer's Guide

This PR modernizes SVG attribute usage by replacing deprecated xlink:href with href throughout the codebase—adding fallbacks in utilities, updating sanitization and paint logic, adjusting dynamic image handling, updating tests and UI fixtures, and bumping related dependency versions.

Sequence Diagram: utilities.getHref Logic

sequenceDiagram
    participant C as Caller
    participant U_getHref as "utilities.getHref"
    participant E as Element

    C->>U_getHref: getHref(E)
    U_getHref->>E: getAttribute("href")
    alt Has 'href' attribute
        E-->>U_getHref: href_value
        U_getHref-->>C: href_value
    else No 'href' attribute
        E-->>U_getHref: null
        U_getHref->>E: getAttributeNS(XLINK_NAMESPACE, "href")
        alt Has 'xlink:href' attribute
            E-->>U_getHref: xlink_href_value
            U_getHref-->>C: xlink_href_value
        else No 'xlink:href' attribute
            E-->>U_getHref: null
            U_getHref-->>C: null
        end
    end
Loading

Sequence Diagram: utilities.setHref Logic

sequenceDiagram
    participant C as Caller
    participant U_setHref as "utilities.setHref"
    participant E as Element

    C->>U_setHref: setHref(E, "new_value")
    U_setHref->>E: setAttribute("href", "new_value")
    E-->>U_setHref: 
    U_setHref-->>C:
Loading

Sequence Diagram: Paint Constructor Gradient Linking Logic

sequenceDiagram
    participant C as Caller
    participant PC as "Paint.constructor"
    participant GO as "options.gradientElement"
    participant Doc as "document"

    C->>PC: new Paint({ gradient: GO_value })
    PC->>GO: getAttribute("href")
    GO-->>PC: hrefResult
    alt hrefResult is not null (href attribute exists)
        PC->>Doc: getElementById(hrefResult)
        Doc-->>PC: referencedGradient
        PC->>referencedGradient: cloneNode(true)
        referencedGradient-->>PC: clonedGradient
        PC->>PC: this.gradient = clonedGradient
    else hrefResult is null (href attribute does not exist)
        PC->>GO: getAttributeNS(XLINK_NAMESPACE, "href")
        GO-->>PC: xlinkHrefResult
        alt xlinkHrefResult is not null (xlink:href attribute exists)
            PC->>Doc: getElementById(xlinkHrefResult)
            Doc-->>PC: referencedGradient
            PC->>referencedGradient: cloneNode(true)
            referencedGradient-->>PC: clonedGradient
            PC->>PC: this.gradient = clonedGradient
        else No link attributes found (neither href nor xlink:href)
            PC->>GO: cloneNode(true)
            GO-->>PC: clonedGradient
            PC->>PC: this.gradient = clonedGradient
        end
    end
Loading

Sequence Diagram: sanitizeSvg Local href Reference Check

sequenceDiagram
    participant SanitizeFn as "sanitizeSvg()"
    participant Node as "Element (e.g., <use>)"
    participant U_getHref as "utilities.getHref"
    participant U_setHref as "utilities.setHref"

    SanitizeFn->>U_getHref: getHref(Node)
    U_getHref-->>SanitizeFn: currentHrefValue
    alt currentHrefValue exists AND is non-local AND Node type requires local href
        SanitizeFn->>U_setHref: setHref(Node, "")
        U_setHref-->>SanitizeFn: 
        SanitizeFn->>Node: removeAttributeNS(XLINK_NAMESPACE, "href")
        Node-->>SanitizeFn: 
        SanitizeFn->>Node: removeAttribute("href")
        Node-->>SanitizeFn: 
        SanitizeFn->>SanitizeFn: Log warning (non-local href removed)
    end

    alt Node is <use> AND getHref(Node) is now falsy
        SanitizeFn->>U_getHref: getHref(Node) // Re-check or use updated state
        U_getHref-->>SanitizeFn: null_or_empty_href
        SanitizeFn->>Node: remove()
        Node-->>SanitizeFn: 
        SanitizeFn->>SanitizeFn: Log warning (<use> element removed)
    end
Loading

Updated Class Diagram: SVG Attribute Handling Modules

classDiagram
  namespace svgcanvas.core {
    class utilities {
      <<Module>>
      +getHref(elem: Element) : string
      +setHref(elem: Element, val: string) : void
    }

    class Paint {
      +Paint(options: Object)
      #linearGradient: Element
      #radialGradient: Element
    }

    class sanitize {
      <<Module>>
      +svgWhiteList_ : Object
      +sanitizeSvg(node: Element) : void
    }
  }
Loading

File-Level Changes

Change Details Files
Consolidate href getters/setters in utilities
  • getHref now prefers href with a fallback to xlink:href
  • setHref switched to setting href attribute directly
packages/svgcanvas/core/utilities.js
Expand sanitizer whitelist and logic for href
  • Added href to SVG element attribute whitelist alongside xlink:href
  • Special-case allow href even without namespace
  • Enhanced warnings to include outerHTML and remove href on non-local refs
packages/svgcanvas/core/sanitize.js
Support href in gradient paint operations
  • Use href or xlink:href to locate gradient source
  • Extract replaced attribute value and clone referenced node
packages/svgcanvas/core/paint.js
Switch dynamic image loading to use href
  • Replace setAttributeNS(xlink:href) with setAttribute('href') on images
  • Handle FileReader result with href instead of xlink:href
packages/svgcanvas/core/svg-exec.js
Update UI code, extensions, and tests to use href
  • Replaced xlink:href in and image elements in fixtures and extensions
  • Adjusted Cypress tests to assert href instead of xlink:href
  • Refactored jGraduate component to set href attribute
cypress/e2e/unit/test1.cy.js
cypress/e2e/ui/scenario1.cy.js
cypress/e2e/unit/utilities-performance.cy.js
src/editor/components/jgraduate/jQuery.jGraduate.js
src/editor/extensions/ext-overview_window/ext-overview_window.js
Bump package dependencies to latest compatible versions
  • Updated svgcanvas, i18next, Babel core/runtime, Cypress, Rollup, and related packages
  • Aligned optional rollup platform package version
package.json
package-lock.json

Possibly linked issues

  • svgcanvas #123: The PR replaces xlink:href with href as requested by the issue to simplify SVGs.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request replaces all remaining uses of xlink:href with href in SVG elements to follow modern SVG standards. The key changes include updates in modules that create or manipulate SVG elements, modifications to the sanitizer utility and test cases, and dependency bumps for supporting libraries.

Reviewed Changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/editor/extensions/ext-overview_window/ext-overview_window.js Updated the element to use href instead of xlink:href.
src/editor/components/jgraduate/jQuery.jGraduate.js Replaced setAttributeNS with setAttribute to update image href attributes.
packages/svgcanvas/core/utilities.js Refactored getHref and setHref functions to prefer href while maintaining backward compatibility.
packages/svgcanvas/core/svg-exec.js Updated attribute modifications in image elements, though one usage remains inconsistent.
packages/svgcanvas/core/sanitize.js Updated whitelist definitions and warning messages to support href.
packages/svgcanvas/core/paint.js Modified gradient extraction logic to support both href and xlink:href.
package.json Bumped versions for svgcanvas, i18next, babel, cypress and related dependencies.
cypress/e2e/unit/.cy.js & cypress/e2e/ui/.cy.js Updated tests to verify changes from xlink:href to href.
Comments suppressed due to low confidence (1)

packages/svgcanvas/core/svg-exec.js:445

  • For consistency with other changes that use setAttribute for the href attribute, consider using setAttribute('href', url) instead of setAttributeNS with NS.XLINK.
image.setAttributeNS(NS.XLINK, 'href', url)

Copy link
@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jfhenon - I've reviewed your changes and found some issues that need to be addressed.

Blocking issues:

  • Incorrect setAttribute usage with namespace URI as name (link)
Here's what I looked at during the review
  • 🔴 General issues: 1 blocking issue, 3 other issues
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

const seAttrNS = (attrName.startsWith('se:')) ? NS.SE : ((attrName.startsWith('oi:')) ? NS.OI : null)
seAttrs.push([attrName, attr.value, seAttrNS])
} else {
console.warn(`sanitizeSvg: attribute ${attrName} in element ${node.nodeName} not in whitelist is removed: ${node.outerHTML}`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Logging outerHTML on every warning may be heavy

Logging the entire outerHTML may generate excessive output and affect performance. Suggest logging only the tag name or a shortened snippet instead.

Suggested change
console.warn(`sanitizeSvg: attribute ${attrName} in element ${node.nodeName} not in whitelist is removed: ${node.outerHTML}`)
const attrValSnippet = attr.value && attr.value.length > 30
? attr.value.slice(0, 30) + '...'
: attr.value;
console.warn(
`sanitizeSvg: attribute ${attrName} (value: "${attrValSnippet}") in <${node.nodeName}> not in whitelist and is removed`
)

} else if (options.linearGradient) {
this.type = 'linearGradient'
this.solidColor = null
this.radialGradient = null
if (options.linearGradient.hasAttribute('xlink:href')) {
const xhref = document.getElementById(options.linearGradient.getAttribute('xlink:href').substr(1))
const hrefAttr =
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Duplicate attribute‐lookup logic

Consider extracting the repeated attribute-lookup logic into a helper function or reusing getHref to reduce duplication and maintain consistent fallback behavior.

Suggested implementation:

      const hrefAttr = getHref(options.linearGradient)
      if (hrefAttr) {
        const xhref = document.getElementById(hrefAttr.replace(/^#/, ''))
        this.linearGradient = xhref.cloneNode(true)
      } else {
        this.linearGradient = options.linearGradient.cloneNode(true)
      }
      // create linear gradient paint
    } else if (options.radialGradient) {
      this.type = 'radialGradient'
/**
 * Helper to get the 'href' or 'xlink:href' attribute from an element.
 * Returns null if neither is present.
 */
function getHref(element) {
  return element.getAttribute('href') || element.getAttribute('xlink:href')
}

@@ -443,7 +443,7 @@ const setSvgString = (xmlString, preventUndo) => {
// const url = decodeURIComponent(m.groups.url);
const iimg = new Image()
iimg.addEventListener('load', () => {
image.setAttributeNS(NS.XLINK, 'xlink:href', url)
image.setAttribute(NS.XLINK, 'href', url)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Incorrect setAttribute usage with namespace URI as name

setAttribute is being used incorrectly here. To set a namespaced href, use setAttributeNS(NS.XLINK, 'href', url). For a non-namespaced attribute, use setAttribute('href', url) instead.

@jfhenon jfhenon force-pushed the prefer-href-to-xlink-href branch from 1c5300b to c9e1668 Compare June 9, 2025 08:38
@jfhenon jfhenon merged commit 865d1be into master Jun 9, 2025
6 checks passed
@jfhenon jfhenon deleted the prefer-href-to-xlink-href branch June 9, 2025 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0