I have a browser extension(For chrome, Firefox, Edge) and now I want to query some info from the extension to the windows service which is running on the same machine.
I need to pass some strings from the extension and the windows service will process these strings and return the processed strings.
I have tried WebAssembly but it does not suit our need because it is compiled code and not running in the background. Also, there are some windows specific headers in the service, and web assembly does not support these headers.
so any idea how can we achieve that?
I will be very thankful to the StackOverflow community.
Advertisement
Answer
You can use native messaging (Chrome docs / MDN)
Create an executable like this:
json_object process_string(std::string_view s) { // Example code std::string result(1000); UINT sz; HRESULT status = CallWindowsApi(s.data(), s.size(), result.data(), 1000, &sz); if (FAILED(status)) { return my_json_library::dict({{"err", my_json_library::integer(status)}}); } return my_json_library::string(result.data(), sz); } int main() { // Make sure stdin is open in binary mode to read the raw bytes of the size _setmode(_fileno(stdin), O_BINARY); _setmode(_fileno(stdout), O_BINARY); while (true) { // Message is prefixed with 4 byte size std::uint32_t size; { char size_buf[4]; if (std::fread(size_buf, 4, 1, stdin) != 1) { return std::feof(stdin) ? 0 : 1; } std::memcpy(&size, size_buf, 4); } // Followed by `size` bytes of JSON json_object obj = my_json_library::read_from_FILE(stdin, size); // Process the recieved message if (!obj.is_string()) return 1; std::string_view s = obj.as_string(); json_object result_json = process_string(s); std::string result = result_json.stringify(); std::uint32_t result_size = result.size(); if (result_size > 1024 * 1024) { // Chrome only allows 1MB messages to be recieved // (Unsure if 1000*1000 (MB) or 1024*1024 (MiB)) // If you might run into this, make a message protocol to // split messages into multiple 1MB chunks } // Sent messages are also prefixed by size if (std::fwrite(&result_size, 4, 1, stdout) != 1) { return 1; } // Followed by JSON data if (std::fwrite(&result.data(), 1, result_size, stdout) != result_size) { return 1; } } }
Register the path of the relevent manifest file using the relevant registry key (probably using an installer to install both the executable and the manifest file + registry key)
And it is relatively simple to call from the chrome extension side:
let port = chrome.runtime.connectNative('com.my_company.my_application'); port.onMessage.addListener(function(msg) { // Handle received message (what your executable writes) if (typeof msg === 'string') { // Success } else { // Error const error_code = msg.err; // ... } }); port.onDisconnect.addListener(function() { // Handle crash (probably just reopen) }); port.postMessage("string to transform"); // Can send any json message, but our executable only handles strings
Or re-running the executable every time (you can remove the loop in the executable if you use this):
chrome.runtime.sendNativeMessage('com.my_company.my_application', "string to transform", function(msg) { // Handle received message (what your executable writes) });
Alternatively, you can run a HTTP server on some arbitrary (but unchanging) port which does this processing, and your web extension can simply POST data to `http://localhost:${port}`
. You should probably allow the host/port to be changed in the config.