I'm porting a Firefox Add-on SDK extension to WebExtensions. Previously I could access the browser's search engines, but now I can't, so a helpful user suggested I try reading the search.json.mozlz4 file, which has every installed engine. However, this file is json with LZ4 compression, and it's in Mozilla's own LZ4 format, with a custom magic number, 'mozLz40\0'.
Before, one could use this to read a text file that uses LZ4 compression, including a mozlz4 file:
let bytes = OS.File.read(path, { compression: "lz4" });
let content = new TextDecoder().decode(bytes);
(although I couldn't find documentation about the "compression" field, it works)
Now, using WebExtensions, the best I could come up with to read a file is
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(ev) {
let content = ev.target.result;
};
This does not handle compression in any way. This library handles LZ4, but it is for node.js so I can't use that. [edit: it works standalone too]. However, even if I remove the custom magic number processing I can't get it to decompress the file, while this Python code, in comparison, works as expected:
import lz4
file_obj = open("search.json.mozlz4", "rb")
if file_obj.read(8) != b"mozLz40\0":
raise InvalidHeader("Invalid magic number")
print(lz4.block.decompress(file_obj.read()))
How can I do this in JS?
After much trial and error, I was finally able to read and decode the search.json.mozlz4 file in a WebExtension. You can use the node-lz4 library, though you'll only need one function -
uncompress
(aliased asdecodeBlock
for external access) - so I renamed it todecodeLz4Block
and included it here with slight changes:Then declare this function that receives a File object (not a path) and callbacks for success/error:
Then you can add an HTML button to your add-on settings page that lets the user search and select search.json.mozlz4 (in WebExtensions you can't simply open any file in the filesystem without user intervention):
To respond to the user selecting the file, use something like this, which calls the method we previously declared (here I don't use the error callback, but you can):
I hope this helps someone. I sure spent a lot of time working this simple thing out. :)