Skip to content
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

Proposal: Add Support for Dynamic SVG Icons in browser.action.setIcon() #462

Open
xeenon opened this issue Oct 3, 2023 · 11 comments
Open
Labels
proposal Proposal for a change or new feature supportive: edge Supportive from Edge supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari

Comments

@xeenon
Copy link
Collaborator

xeenon commented Oct 3, 2023

Currently, the browser.action.setIcon() API and the manifest file format support setting extension icons via local image paths or dynamically via ImageData. These methods are effective but lack native support for dynamic vector images like SVGs, which offer scalable quality. To enhance these capabilities, I suggest extending both the API and manifest specifications to accommodate data URLs for dynamic SVG images.

Note: While this proposal aims to support data URLs, other types of URLs such as HTTP or HTTPS would not be supported, ensuring all resources remain local to the extension.

Dynamic Runtime Icons

Developers can use a mix of smaller PNG icons and larger dynamic SVGs:

const pngDataURL = canvas.toDataURL('image/png');
const svgDataURL = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><rect width="48" height="48" rx="8" ry="8" style="fill: green"/></svg>';

await browser.action.setIcon({ path: { '16': pngDataURL, '48': svgDataURL } });

Manifest Icons

Data URLs can also be used directly in the manifest:

{
  "manifest_version": 3,
  "action": {
    "default_icon": {
      "16": "data:image/png;base64,...",
      "48": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'>...</svg>"
    }
  }
}

While single-file deployment may not be ideal for production, it is beneficial for testing scenarios and ensures feature parity with the action.setIcon() API. Data URLs could also be used in manifest sections that currently accept resource paths, like content scripts, broadening the range of options for specifying resources during testing.

Other Considerations

Specifying SVG or Canvas Elements

Developers could directly specify SVG or Canvas elements in the setIcon method using a new element key. The browser would serialize the element into the required image data.

const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
// Add shapes and styles to the SVG.

await browser.action.setIcon({ element: { '48': svgElement } });

Note: This approach is limited to page contexts, as service worker background scripts lack DOM access. It's therefore unsuitable for extensions that rely solely on service workers for background tasks.

This method simplifies the serialization process but restricts icon creation to specific web elements like SVG and canvas.

Compatibility Considerations

This proposal builds upon existing APIs in a backward-compatible manner. However, data URLs may not be universally supported. Developers should test compatibility and may need to provide fallbacks.

Note: Introducing a new element key could mitigate compatibility issues. Older browsers would hopefully ignore this new key, simplifying the transition without breaking existing setups. It also allows for using path or imageData as fallbacks alongside the new element option.

@xeenon xeenon added supportive: safari Supportive from Safari proposal Proposal for a change or new feature labels Oct 3, 2023
@erosman
Copy link

erosman commented Oct 3, 2023

While on the subject of action.setIcon() ...

@tophf
Copy link

tophf commented Oct 4, 2023

This approach is limited to page contexts, as service worker background scripts lack DOM access

You can allow OffscreenCanvas to support service worker context.

@hanguokai
Copy link
Member

Which is the point of this issue?

  1. Support SVG (both static and dynamic SVG)
  2. Support dynamic SVG (rather than static SVG)
  3. Support data url (for SVG and other image format)

What is the definition of dynamic SVG? Generate SVG file content using JavaScript?

Manifest is a static file, so it is not possible to support dynamic SVG, only static SVG. I think supporting file path in manifest is sufficient, there is no need to use data url.

For action.setIcon(), I prefer this design:

// specify file path
action.setIcon({ path: 'icons/icon.svg' });

// specify svg content, no need to specify size or convert to other formats
action.setIcon({ svg: 'raw svg content string' }); 

BTW: Does SVG icon support animation?

@xeenon
Copy link
Collaborator Author

xeenon commented Oct 4, 2023

Generating SVG content using JavaScript is the main goal. Dynamic bitmaps are already supported with ImageData — my proposal would be useful there too, but not a main goal.

I agreed in the proposal that data URLs in the manifest have limited utility, but can be useful for writing small single-file tests.

SVG does support animation, but they are not be supported in icons (at least in Safari).

action.setIcon({ svg: '<svg>...</svg>' }) is a valid option to consider. While SVGs can be one file fits all, there are still cases where multiple SVGs are desired. Often designers will simplify or add more detail based on the intrinsic size of the SVG. So supporting multiples sizes would still be desired, in my opinion.

@dotproto
Copy link
Member

dotproto commented Oct 4, 2023

I've been interested in seeing SVG support for extension and action icons for a while, but to be honest I hadn't really considered the possibility of using the SVG as a source used to generate static raster assets that would be used in browser UI. I think this path has a lot of potential.

SVG would allow developers to supply high resolution icons that would render well at a variety of sizes and DPI. It enables browsers to explore more ways of presenting an extension's visual identity without compromising image quality. And the dynamism of a vector format may help us address other challenges, such as adapting to light/dark mode or a user's theme. On that last point, use of currentColor or env() values would be particularly helpful.

I agree with @xeenon's comment that supporting data URLs is "beneficial for … feature parity with the action.setIcon() API." In general, I would like to move towards fewer concepts that behave more consistently across WebExtensions APIs. This practice makes it easier for new developers to learn, for existing developers to predict how different systems will behave, and (hopefully) for browser teams to maintain.

This approach is limited to page contexts, as service worker background scripts lack DOM access

You can allow OffscreenCanvas to support service worker context.

While you can use an OffscreenCanvas to draw an image worker contexts, as far as I know it's not possible to draw an SVG without using DOM methods. The only solutions I've seen for drawing SVGs to a canvas use either, document.createElementNS("http://www.w3.org/2000/svg", "svg") as @xeenon suggested, new Image() as seen below, or even less common approaches like innerHTML and DOMParser.

let canvas = new OffscreenDocument(300, 150);
let ctx = canvas.getContext('2d');

let img = new Image();
img.onload = () => ctx.drawImage(img, 0, 0);
img.src = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><rect width="48" height="48" rx="8" ry="8" style="fill: green"/></svg>';

The only way I can see to do this in a worker is with a coordinating document context that transfers an ImageBitmap to the worker.

For action.setIcon(), I prefer this design:

// specify file path
action.setIcon({ path: 'icons/icon.svg' });

// specify svg content, no need to specify size or convert to other formats
action.setIcon({ svg: 'raw svg content string' }); 

I'm hesitant about setting a single SVG without any size constraints. Icons sometimes require special considerations when rendered at small sizes. See the Pushing Pixels blog post About those vector icons for a good discussion of this topic.

@hanguokai
Copy link
Member

Currently both path and imageData parameters in action.setIcon() support input data or dictionary. So svg parameter can follow the same style.

// specify a single SVG for lazy developers
action.setIcon({ svg: "<svg>...</svg>" }); 

// specify different sizes of SVG for demanding designers
action.setIcon({
    svg: {
        "16": "<svg>...</svg>",
        "24": "<svg>...</svg>",
        "32": "<svg>...</svg>",
        "64": "<svg>...</svg>"
    }
}); 

@erosman
Copy link

erosman commented Oct 6, 2023

There are other issues to consider with regards to SVG & the background Service Worker in Chrome:

@zombie zombie added the supportive: firefox Supportive from Firefox label Oct 12, 2023
@mukul-p mukul-p added the supportive: edge Supportive from Edge label Oct 12, 2023
@zombie
Copy link
Collaborator

zombie commented Oct 12, 2023

Firefox would support this, though preferably if possible to avoid data: URIs, perhaps by using fragment info inside SVG to create parametrized images, or blob URIs to support fully dynamic use cases.

@Rob--W
Copy link
Member

Rob--W commented Oct 12, 2023

or blob URIs to support fully dynamic use cases.

Since blob:-URLs are tied to the context they're associated with, we'd need to be careful with ensuring that the URL remains valid even after its revocation. Or alternatively, consider that a feature and allow extensions to free up the image data without explicitly clearing the badge image?

@carlosjeurissen
Copy link
Contributor

Something which might be overlooked and could be useful in the case of action icons for webExtensions is the svg element:
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/switch

Some quick testing reveals:
Firefox supports the element both for SVGs on webpages as for extension action icons with support for subtags (like en-GB).

Safari supports the element only for SVGs on webpages but not for extension action icons. It also does not seem to support subtags like en-GB.

Chrome somehow selects the wrong language and as we know Chrome does not yet support SVGs for extension icons.

@xeenon
Copy link
Collaborator Author

xeenon commented Sep 21, 2024

@carlosjeurissen Safari uses a small subset of SVG that is supported by the system, which is more limited than what WebKit can render.

Any further proposal here should build upon icon_variants which added the any size for SVG. I think localization of icons could be handled as an additional locale key in the specified variants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Proposal for a change or new feature supportive: edge Supportive from Edge supportive: firefox Supportive from Firefox supportive: safari Supportive from Safari
Projects
None yet
Development

No branches or pull requests

9 participants