宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
Recently I wrote a blog entry for developers of addons for Firefox, who are planning to migrate his/her XUL-based addon to WebExtensions. This is the full version which includes some side topics not related to WebExtensions. I hope this helps you to migrate your ancient addon to WebExtensions.
Hello, addon developers. My name is YUKI Hiroshi aka Piro, a developer of Firefox addon.
For long years I developed Firefox/Thunderbird addons personally and on business, based on XUL and XPCOM. By some reasons I didn't migrate my addons from such a legacy style to SDK-based, but recently I've started to research what APIs are required to migrate my addons to WebExtensions, because Mozilla announced that XUL/XPCOM addons will be ended at the end of 2017. And I realized that some addons are possibly migretable only with currently available APIs. The Popup ALT Attribute is one of such addons.
Recently I've successfully done it, so let's describe how I did that.
At first I explain what the addon Popup ALT Attribute is.
It is one of ancient addons started on 2002, to show what is written at the alt
attribute of img
HTML elements in webpages.
By default Firefox shows only the title
attribute as a tooltip.
For example:
<img src="dog.jpg" title="a photo of a dog">:
A tooltip is shown with a text "a photo of a dog".
<img src="dog.jpg" alt="(a photo of a dog)">:
No tooltip is shown by default.
With the addon, a tooltip appears with a text "(a photo of a dog)"
like ancient Netscape Navigator 4 or MSIE.
Initially the addon was implemented to replace an internal function FillInHTMLTooltip()
of Firefox itself, with my custom version.
At April 2011 I migrated it to a bootstrapped extension.
Instead of replacing/redefining the internal function directly, it became to be triggered by the popupshowing
DOM event, and canceled the event by Event.prototype.stopPropagation()
to override Firefox's default behaviors.
Because the change is not destructive, it could be uninstalled safely.
I think it was the baseline of the migration for WebExtensions. Even if you don't migrate to bootsrapped, I strongly recommend you to rewrite your addon only with such safer method, without any destructive changes - XUL overlaying, function replacement, and so on.
(In other words, if your addon is not migratable to bootstrapped, then you possibly have to wait that some new WebExtensions APIs are landed on Firefox itself. Sadly some my addons are here...)
At Febrary 2016 I migrated it to ready for e10s. To be honest it was the largest barrier on my case. Through migration of Firefox itself for e10s, implementation to fill the tooltip was moved to the lower layer written in C++ and I had to give up the old approach which overrides the partial operation to construct tooltip content, because all operations to construct tooltip were completely enclosed in the low layer.
Instead I noticed that Firefox always shows tooltip when an HTML element has its own title
attribute.
So, I decided to copy the value of alt
attribute to the title
attribute for hover-ed HTML elements themselves, when any mousemove
event is fired in webpages.
For example:
Before:
<img src="dog.jpg" alt="(a photo of a dog)">:
After:
<img src="dog.jpg" alt="(a photo of a dog)" title="(a photo of a dog)">:
As the result, most codes were successfully separated from the main script (executed in the chrome process) to a frame script (executed in the content process), and the main script became just a loader for the frame script.
You may not need to migrate your addon for e10s for now, because your addon will become e10s friendly after successful migration to WebExtensions. But you still need to isolate your codes from Firefox's internal operation flow. We XUL/XPCOM-based addon authors know Firefox's internal implementation deeply, so sometimes we think like a developer of Firefox and try to implement an addon just like a "patch". However, in WebExtensions world we are not allowed to access internal opeartions of Firefox itself. Please forget your existing knowledge about inside of Firefox - now you must think how to get what you want only via public APIs. In other workds, you must change your mind from "how to inject my operations into the operations flow of Firefox itself partially?" to "how to reuse Firefox's known behavior and get the result what I really want, only with public APIs?"
(Yes, if your addon strongly depends on Firefox's internal functions, then you have to wait new standard Web APIs or Web Extensions APIs. Most of my addons are here.)
I read the tutorial to build a new simple WebExtensions-based addon from scratch before migration. And I realized that bootstrapped extensions are similar to WebExtensions addons:
My addon was easily re-formatted as an WebExtensions addon, because I already migrated it to bootstrapped.
This is the initial version of the manifest.json
I wrote.
There were no localization and options UI:
{
"manifest_version": 2,
"name": "Popup ALT Attribute",
"version": "4.0a1",
"description": "Popups alternate texts of images or others like NetscapeCommunicator(Navigator) 4.x, and show long descriptions in the multi-row tooltip.",
"icons": { "32": "icons/icon.png" },
"applications": {
"gecko": { "id": "{61FD08D8-A2CB-46c0-B36D-3F531AC53C12}",
"strict_min_version": "48.0a1" }
},
"content_scripts": [
{ "all_frames": true,
"matches": ["<all_urls>"],
"js": [
"content_scripts/content.js"
],
"run_at": "document_start" }
]
}
My addon had a frame script and a loader for it.
On the other hand, manifest.json
can have some manifest keys to describe how scripts are loaded.
It means that I don't need to put my custom loaders in the package anymore.
Actually, a script for any webpage can be loaded with the content_scripts
rule in the above sample.
See the spec of content_scripts
for more details.
So finally only 3 files were left. Before:
+ install.rdf
+ icon.png
+ [components]
+ [modules]
+ [content]
+ content-utils.js
And after:
+ manifest.json (migrated from install.rdf)
+ [icons]
| + icon.png (moved)
+ [content_scripts]
+ content.js (moved and migrated from content-utils.js)
And I still had to isolate my frame script from XPCOM.
nsIPrefBranch
and some XPCOM components via XPConnect, so they were temporarily commented out.Ci.nsIDOMNode.ELEMENT_NODE
had to be replaced as Node.ELEMENT_NODE
.mousemove
events from webpages was attached to the global namespace for a frame script, but it was re-attached to the document
itself of each webpage, because the script was now executed on each webpage directly.For the old install.rdf
I put localized description.
In WebExtensions addons I had to do it in different way.
See how to localize messages for details.
In short I did followings.
Added files to define localized descriptions:
+ manifest.json
+ [icons]
+ [content_scripts]
+ [_locales]
+ [en_US]
| + messages.json (added)
+ [ja]
+ messages.json (added)
Note, en_US
is different from en-US
in install.rdf
.
English locale, _locales/en_US/messages.json
was:
{
"name": { "message": "Popup ALT Attribute" },
"description": { "message": "Popups alternate texts of images or others like NetscapeCommunicator(Navigator) 4.x, and show long descriptions in the multi-row tooltip." }
}
Japanese locale, _locales/ja/messages.json
was also.
And, I had to update my manifest.json
to embed localized messages:
{
"manifest_version": 2,
"name": "__MSG_name__",
"version": "4.0a1",
"description": "__MSG_description__",
"default_locale": "en_US",
...
__MSG_****__
in string values are automatically replaced to localized messages.
You need to specify the default locale manually via the default_locale
key.
Sadly Firefox 45 does not support the localization feature. You need to use Nightly 48.0a1 or newer to try localization.
Currently WebExtensions does not provide any feature completely compatible to nsIPrefBranch
.
Instead there are simple storage APIs.
It can be used like an alternative of nsIPrefBranch
to set/get user preferences.
This addon had no configuration UI but had some secret preferences to control its advanced features, so I did it for future migrations of my other addons, as a trial.
Then I encountered a large limitation: the storage API is not available in content scripts. I had to create a background script just to access the storage, and communicate with it via the inter-sandboxes messaging system.
Finally, I created a tiny library to do that. I don't describe how I did it here, but if you hope to know details, please see the source. There are just 177 lines.
(Updated at 2016.4.26: bug 1197346 has been fixed at Nightly 49.0a1 so you don't need any library to access the storage system from content scripts anymore. Now, my library just provides easy access for configuration values instead of the native storage API.)
I had to update my manifest.json
to use the library from both the background page and the content script, like:
"background": {
"scripts": [
"common/Configs.js", /* the library itself */
"common/common.js" /* codes to use the library */
]
},
"content_scripts": [
{ "all_frames": true,
"matches": ["<all_urls>"],
"js": [
"common/Configs.js", /* the library itself */
"common/common.js", /* codes to use the library */
"content_scripts/content.js"
],
"run_at": "document_start" }
]
Scripts listed in a same section share a namespace for the section.
I didn't have to write any code like require()
to load a script from others.
Instead I had to be careful about the listing order of scripts - put a script requiring a library after the library itself, in each list.
One left problem is: how to do something like the about:config
or the MCD - general methods to control secret preferences across addons.
For my business clients, I ordinarily provide addons and use MCD to lock their configurations.
(There are some common requirements on business use of Firefox, so combinations of addons and MCD are reasonable than creating private builds of Firefox with different configuration for each client.)
I think I still have to research around this point.
WebExtensions provides a feature to create options pages for addons. It is also not supported on Firefox 45, so you need to use Nightly 48.0a1 for now. As previously I told, this addon didn't have its configuration UI, but I newly implemented it as a trial.
In XUL/XPCOM addons rich UI elements - <checkbox>
, <textbox>
, <menulist>
, and more - are available, but as I told, XUL is going to end.
So I had to implement custom configuration UI based on pure HTML and JavaScript.
(If you need more rich UI elements, some known libraries for web applications will help you.)
On this step I created two libraries:
As above, I've successfully migrated my Popup ALT Attribute addon from XUL/XPCOM to WebExtensions. Now it is just a branch but I'll release it after Firefox 48 is released.
Here are reasons why I could do it:
However, it is a rare case for me. My other 40+ addons require some privilege, and/or they work outside the content area. For example:
<br>
s will produce virtual line break. Range.prototype.toSring()
does not refrect them.) and something like RangeFinder.Currently supplied WebExtensions are based on Google Chrome's spec, and they are designed just to implement some typical type extensions. On the other hand, we sometimes developed non-typical addons based on our imagination, because XUL-based addons can work like dynamic "patch" around running codes of Firefox. Most of my cases are such non-typical addons. I have to do triage, plan, and request new APIs not only for me but for other XUL/XPCOM addon developers also.
Thank you for reading.
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2016-04-19_webextensions.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.