Skip to content
Advertisement

Communication between browser extension and windows service

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.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement