Please welcome Rob Esimione.
Thank you. Simon. Simon. Simon.
Okay. Is it working? Yes, I think we're all good to go. Okay.
Hello. Welcome. Thank you for coming. My name is Simeon Vincent. I'm a developer relations engineer working on Firefox add-ons.
Who are you? I'm Rob Hul. I'm working on the web extensions team at Firefox on extension APIs that can be used to build extensions.
Today we're going to be talking a bit about building cross browser extensions in 2024 manifestv3, cross browser web extensions, all that good stuff.
In case you're somehow not familiar with extensions, they're personally one of my favorite aspects of the web platform. Web extensions are little programs essentially that allow you to customize how the browser looks and feels.
A couple of examples. Tree style tabs will add a tree of pages as you browse the web on the left side or I guess in the sidebar.
You block origin probably needs no introduction with this crowd. So it is a content blocker or a content blocker that allows you to block pieces of functionality on web pages.
And Dark Reader as I kind of contrast to some of these other things allows you to have your preferences. Like if you prefer using dark mode, Dark Reader will enable that on websites, even if the websites don't support or the website author didn't create a dark theme for your ideal website.
Web extensions are cross browser extension model originally developed by Chrome and now all browsers are aligning around this particular approach to designing extensions.
Over the years there have been a bunch of different ways that browsers have tried to provide ways of customizing or personalizing the browser experience. And the web extension model is particularly interesting because it is built using web technologies.
It's built on top of the web platform itself. So when you customize your browser using a web extension, the author was using HTML, JavaScript and CSS to build that experience.
Web extensions as a cross browser thing originally started with Firefox, building a new extension platform based on Chromium. And since then Safari has adopted it and it's before Edge adopted Chromium as its base, they also adopted the web extension model.
So this is, while it's not like a standard in the W3C or other standard group, it is a cross browser effort. All of us are working together in the web extension community group in the W3C.
And in addition to browsers, the community is also involved providing feedback and helping evolve and enhance the extension platform.
So today we're going to take a closer look at how to build some extensions with a mindset towards maximum compatibility with current browsers with new form factors like Firefox for Android.
You're now able to have your web extensions available on your mobile phone. And how to kind of maximize compatibility as you build out a new browser extension.
Okay, so as the first demonstration, we are going to show how to modify a website. That's a very common task. Most extensions that are just starting out want to enhance a website in some way.
So this is a very simple extension. This consists of two files. A manifest or JSON file that declares some metadata such as a name and version of extension.
Not very important for the rest of this talk. Something about the manifest version which will be more significant as we see later.
And the context scripts entry that declares which file should run and where. In this case, it declares to run on every side.
The exact demo doesn't really matter. In this case, we are just going to register a click handler on the document that shows a dialogue when you click it.
And now we're going to try a live demo.
So first, oh, can you connect your Android phone?
Oh, yes.
Okay, while he's connecting his Android phone, I'm going to type the comment.
So there's a WebEx tool that is a program that allows you to run or build extensions very easily.
It takes care of looking up the location of the browser bindings and packaging the extension and launch it without effort.
So WebEx run is the common to run extension in the current directory. T is target.
In this case, we are starting at Firefox and red.
And then let's see. Does it do anything?
Okay, so yeah, I'll show that later.
Okay, well, I'm at it.
I'm just starting to clean copy in background.
So this is a Firefox for Android.
Currently showing something.
And I ran the comment before it showed that some parameters were missing.
In this case, we need to select which device is being used.
And then it still doesn't work.
Those on Firefox for Android on Android device are multiple Firefox for Android applications.
The IDs are listed there for convenience.
In this case, I'm going to select nightly.
So the Phoenix is the code name for nightly and Firefox 10 for the release version.
There are more things such as pet and even non Firefox builds.
Okay, I launched it.
There's a request to install extension.
I click somewhere and I get a dialogue as expected.
So now I'm going to repeat the same for desktop just to see how easy it is.
I could type Firefox desktop.
By default, it launches Firefox desktop because initially that is the goal of that X.
Later, we expanded it to older browsers.
Example.com.
Typos.
It works again.
And for completeness also Chromium.
Example.com.
And it works.
It takes the load the menu at this point.
Yes.
Okay.
I'm going to back to the slide for a bit.
Okay.
And now to the demo.
So as you can see from the extension management page.
It's possible to load it manually.
Just for the sake of demonstration, I'm going to remove extension that were installed by the tool.
Oh, okay.
Sorry about that.
I'm going to manually load extension.
There was the original one we loaded.
Manifest.
All files are there.
And notice that there's some error.
So if you increase the size, I'll just pop in.
Go ahead.
Explain the spectrum.
So at the moment, Chrome has formally dropped support for Manifest v2.
Or that's not quite, it's deprecated support for Manifest v2.
So it's no longer possible to publish a new extension in the Chrome Web Store using Manifest v2.
And in fact, it's been about a year since it was supported.
In over the course of the remainder of this talk, we're going to be focusing specifically on Manifest v3.
Because if you're building a new extension, you have to use it.
And as you clearly see this Chrome, it can still load Manifest v2 extensions.
And of this year, they will start experimenting to turn off Manifest v2 support.
At which point it's not possible to load Manifest v2 extension in Chrome any longer.
Firefox still continues to support Manifest v2.
It hasn't deprecated Manifest v2 yet.
So again, just load extension Firefox just fine.
I think it's also worth noting that Firefox does not yet have a deprecation timeline for Manifest v2 either.
Whereas Chrome has a kind of end of life timeline formally announced and is coming in the next six months.
So the way to fix this would be to switch to Manifest v3.
The question is how to do that.
So like you see, there's an error here.
There was the original code that we started with.
Manifest v2 was mentioned there.
So we have to do Manifest v3 and then we try again.
Just for the sake of, since I've opened anyway, I'm loading Manifest v3 extension.
Okay. And this was the Manifest v2 one.
I'm removing it since I don't care about it.
This Manifest v3.
Okay. There was an old example, the new one.
Again.
If I view this page, there's still some errors here.
I'll get to that later.
Yeah.
The next part of extension model that is very important are host missions.
In a previous example, we ran the extension on all websites.
We requested to do so on our website and we bumped the extension Manifest v3 and it still worked in Chrome.
What it didn't show yet is how to run extension on Firefox.
Well, I did show how to, but not that I did so.
So let's try again.
As Rob does this up, one of the notable differences between in Firefox's support for Manifest v3 is
host permissions are limited by default.
We want to encourage developers to go in the direction of giving users more control over where and when
extensions run.
So by default, the set of hosts that the extension declares aren't granted by default.
Thanks.
So at this time Manifest v3 extensions do not get the permissions unlike Chrome.
That's a common source of confusion for extension developers.
And as you just saw, I launched Firefox again with a Manifest v3 extension.
You click around, nothing happens.
What you do see is that there is built-in extension UI in Firefox to control permissions.
So if you enable permission from this point and load it again, it will work.
And so that is the built-in UI in Firefox.
Chrome has similar UI to disable it.
So even if you currently think that your extension works in Chrome, it may not if, for example,
the user has disabled it or maybe sometime in the far future Chrome has also followed a similar approach.
It's also worth noting, even though we only show Firefox and Chrome here in today's presentation,
that Safari has also something special for host permissions.
In fact, all host permissions are disabled by default.
Safari takes the most strictest approach to host permissions for privacy reasons,
meaning that you have to explicitly interact with the browser UI to allow the extension to run on the page.
In a previous slide, I showed built-in browser UI to do so.
Now I'm going to show how to do it in your own extension with the right context to convince the user to give you those permissions.
To do so, in the Manifest v3 declared options UI page,
in this very simple extract of the options page, I show a label.
This is just a very simple explanation.
In reality, you may convince the user that they will win something.
I mean that they will get some useful functionality.
Please do not mislead the user.
And for the sake of this example, a checkbox to send the permissions.
For the demonstration, I'll be very to the point.
Check in the checkbox will give extra to example.com.
In your own extension, you may use a more descriptive, non-technical description.
And finally, I load the options.js file.
In options.js file, I declare the permission that I want to request.
The permission follows the format that's shown on the screen.
The origins key of the object to declare list of match patterns,
which you saw before in the Manifest file.
Match patterns are the common way in extensions to define on which host an extension operates.
In this case, in this context, with permissions, the path is ignored.
Because when you request access to example.com, it doesn't really matter
whether it's example.com slash favicon or example.com slash index.
It's also worth noting the scheme in this case.
The wild cards are a little special in the way that they work.
They're not, you know, general glob patterns.
Scheme, the asterisk here specifically means HTTP and HTTPS.
It doesn't include filed URLs.
And the asterisk in the domain or in the host section,
it can only be a prefix.
It can't pattern match on the top level domain or in the middle of a pattern.
So it can only specify subdomains.
And in this case, asterisk. includes both subdomains and example.com.
Thank you.
Okay. So the UI was very simple.
We had a checkbox and a label that you can click that triggers the checkbox.
To start with, we will first make sure that the checkbox reflects the actual state of the permissions that you have.
So you will request, well, check the permissions that you have and update a checkbox state.
Very useful.
Now you need to make sure that when the checkbox is ticked, that the permissions are actually granted.
To do so, we introduce a change listener.
A change listener is as fired when the checkbox state changes.
When the checkbox itself is checked, so that means that the user checked the checkbox,
we want to make sure that permissions are granted if they have not been granted yet.
So we use the permissions.request API to do so.
And if somehow the user dismisses the permission request, we uncheck the checkbox to make sure that the UI is consistent with what actually happened.
To remove the permission, we do something similar.
You'll notice that in this case, I'm using try catch because Chrome has something special about it, which we'll explain a few minutes later.
And finally, also note that I use permissions.onEdit and permission.onRemoveTheVentListeners here.
These are triggered when the permission change, not just by the checkbox here, but also if you use built in browser UI,
or simply have opened the tab in a different page.
It's a very common mistake of extensive developers to not account for changes in other tabs or other external factors.
So make sure that if you have some UI or settings page, synchronize it with your other potential external triggers.
I'm going to demonstrate it.
Oh, just a bonus tip.
So in the demo here, we all, well, clearly you can see that the browser is using dark theme.
By default, the web page and Accenture page include that use a white, well, a light theme,
which is one simple trick, adding a made and named call scheme with content dark is and light.
You can automatically get the dark theme support for your extension page.
Even if you don't develop any extensions, take away this tip for your own web applications.
With very little effort, you can get dark theme support automatically without any extra extensions.
I actually learned about this from Rob earlier today.
Right. So if you click on the check box, you'll see that permission request comes up and then you can choose to allow or disallow.
Will you continue?
Did you also want to show it on Android?
Oh, yes. Thank you.
So on Android, the flow is also similar.
You find the settings page as you can see through these arrows.
And then you can also grant the permission to similar UI.
I could do a live demo, which is for the sake of time.
I'm just going to the screenshots.
If people are following this later, the slides are also published later with screenshot included.
So you don't have to watch the whole video recording.
So this is the Chrome page.
Okay, that was the special part with Chrome that I'm going to show now.
So I can do this too by recycling real但.
So previously I said there was some try catch to handle some error in Chrome.
The reason for that is, I just wanted to make it bigger.
Okay, yeah, it's bigger.
Thank you.
If you click here you'll see that nothing happens for some strange reason.
The reason for that is that in Firefox you can always, the user can always toggle permission
and extension can do the same.
In Chrome an error occurs when you try to remove permission that is declared in the
counter scripts.
Because the extension claims that it's required permission and Chrome refuses to remove it.
You can still trigger the same flow by using the side axis control that I mentioned before.
So there's the built-in UI from Chrome to control axis.
There are several ways including on click.
So now I try again and I see the checkbox is unchecked.
I can check it, permission request appears, approve it.
Okay now I'm locked in the jail.
But the important thing there is, again, the permissions can be revoked outside of your
extensions flow and you might not otherwise have signals to it.
So it is critical to subscribe to those events and make sure that you're reacting to them
appropriately.
Right, so in this example that we looked at so far, we've statically declared our content
scripts and had them automatically run on the page and then respond to the invocation
on click.
But if we really want to embrace user control, user privacy, it is best to react to a user's
invocation of the extension and then perform the work that you need to do rather than always
statically injecting and potentially having access to sensitive information that you don't
actually want.
So both as a user, you're protecting your own data, as a developer, you're respecting
the user and making sure you don't have access to content you don't need.
So the pattern that we want to encourage is that kind of in-context reaction and we do
that through a special permission called ActiveTab.
So in this demo, we're going to be using the ActiveTab permission to detect when we're
in vote and then get temporary access to the page and then we're going to be using the
scripting permission to execute the script in context.
And we're going to be doing that using the extensions action icon, the thing that appears
in the toolbar that identifies your extension.
It's also worth noting that declaring action in this file is necessary.
If you don't, you can't actually use that API.
So it's kind of an implicit permission.
In this demo, we're going to be targeting specifically Firefox.
So we're going to start with a scripts array.
This is kind of the more traditional approach.
It uses a background page, an event page in order to run the script.
We are going to be targeting Chrome in just a little bit.
So we're going to kind of ahead of time polyfill the browser namespace.
The Chrome version of the extension platform uses the global Chrome and Firefox when it
implemented the web extension APIs.
It used the namespace browser.
And the big difference between the two is that browser supports promises and Chrome historically
did not.
It does in manifestv3.
So we're using this quick polyfill.
If you are targeting manifestv2, you'll need a larger, more extensive polyfill.
So as described, when the user clicks the action, we will execute a script.
We will inject on the current tab, and we're just going to alert.
We're going to skip over that for time.
Skip the demo.
Yeah, skip the demo.
You get the idea.
It works.
So when we target Chrome, we're going to actually need to use a different entry point into the
background context.
We previously used scripts.
That's what was used with pages.
Chrome no longer supports background pages.
It only supports a service worker.
So we need to declare a different entry point.
Again, you believe me that it works.
The important thing now is we have two different entry points.
And ideally, as an extension developer, you don't want to deal with having a bunch of
different files in special configuration per browser.
So in very recent versions of Chrome and Firefox, it's possible to declare both strips
and service worker in the same manifest file.
But because this is so recent, we don't want users with older versions of Firefox or Chrome
to get these updates without, and have the extension fail to load and run.
So we're going to add some guards to our manifest here.
We have four Firefox.
We have the gecko and gecko Android keys and the strict min version.
It just coincidentally in both browsers is version 121.
There's no association beyond that.
And critically in Firefox, the ID field is required when uploading a manifest v3 extension
to AMO.
And then in Chrome, we target, again, 121.
And that all works as expected.
Wonderful.
Over here, I want to highlight that scripts is an array.
So it's possible to declare multiple files.
And so you can have libraries or chunk your application up into different pieces in order
to better organize your code, whereas service worker is a single entry point.
So it's possible to achieve this multi-file loading thing with a slight tweak.
In this case, we can use like a service worker JS file as your entry point.
And then in that file, we're just going to use import scripts, which is special to worker
contexts.
It will synchronously import these scripts and execute them.
So we can achieve the same result with, again, a single manifest file and a slight tweak
to how we load service workers.
We are going to skip this part.
Basically another way to get the same functionality without the background script, if you care
about cross-platform stability, but without dropping support for older versions, you
can get rid of the background script and use a pop-up that opens when the button is clicked
and close the pop-up.
So let's talk about short-lived background context.
In manifestv3, one of the major changes to the extension platform is in the past we had
persistent background pages, and that's no longer an option for constraints outside of
the browser's control.
In order to be able to run everywhere and run efficiently, we necessarily limit when
an extension, we terminate idle extensions background contexts.
That has some important consequences for how you design and build your extension.
So we're going to very quickly skip towards the end because of time.
We have prepared, like, for over an hour, I guess, a presentation.
So if this kind of interests you, you can look up the slides from the FOSTEM link.
The speaker notes are also included.
We don't see them here, but we make sure that the slides are understandable even without
the video.
Yes.
It's also, I think, worth noting we're planning to expand on this and do, like, a webinar
later to share some of this content and explore it with less time constraint.
So hopefully we'll see you tuning into one of those sessions and answer some questions
from you all about building browser extensions.
Thank you.
Bye.