(Image from Firefox wallpapers – November 2017)

WebExtensions is a technology that is used to develop Firefox extensions that are highly compatible with other browser Extension interfaces (e.g., Chrome Extension API, Edge).

For a tutorial on WebExtensions, visit the MDN and Firefox community: This article describes the Native Messaging section of the WebExtensions extension, that is, the browser extension communicates with the Native application, and traces a development implementation of an extension Firefox shortcut that requires Native Messaging.


background

Because Firefox 57+ deprecated all extensions except WebExtensions, many of the extensions of the utility legacy technology needed to be migrated to WebExtensions. As a result, migrating the extended Firefox shortcut to WebExtensions became one of the tasks.

The extension firefox Shortcut does this in a nutshell:

Open calculator, Notepad, draw, and more in Firefox

Of course, only Windows is supported.

But unfortunately, the WebExtensions API does not provide such a delicate interface to directly call up programs in the system. The only way that came to mind was Nativing Messaging.

Nativing Messaging

Put an.exe file on the user’s computer, and the extension calls up the.exe file and communicates with it

The extension, combined with Native Messaging development, almost removed the limitations of the WebExtensions API. So firefox shortcuts can also be implemented.

To prepare

There are two tutorials about Nativing Messaging on MDN:

  • Native messaging
  • Native manifests

One on principle, one on configuration. The English was a little weak, so I translated the article into Chinese while researching:

  • Communicates with the local application
  • Native App List

According to the research, to make the extension with Native Messaging run, you need to:

  • An extension (duh)
  • A native application that can be called up (such as Windows.bat,.exeFile)
  • – Native apps configured correctly (Windows is modified registry)

Are not difficult to achieve, the right guide to the user. Unfortunately, I’m a Web front-end, so I can’t write a compilation language that can be packaged as.exe.

  • JavaScript packaging.exeHuge volume
  • Windows doesn’t support Python natively (and I won’t)
  • C/C++ is mostly forgotten and returned to the school

So… When in Rome, I learned Rust:

  • Rust programming language

So, the next native example will be written in Rust.

You may not know Rust or even have never heard of it, but Rust, like C, is a very low-level language. Through this article, you can learn more about the underlying implementation of Messaging rather than Rust itself.

Here is a joke: the Native end of the example given in the MDN article is written in Python, and there is almost only one piece of code without much explanation. This caused a lot of trouble for non-Python writers — there was no way to find Messaging details by reading Python code. Of course, this article does not have that problem.

The development of

Browser section

Native Messaging using the interface is the runtime. SendNativeMessage, way and the runtime. SendMessage almost no difference, just sent to the object is different. (And of course, connection-based messaging.)

So I first restored the Borwser Action and Popup layer of the firefox shortcut in the browser section.

Of course, with a few features removed, the Firefox shortcut now opens only four of the apps pictured here.

There’s nothing to say about UI implementation. Binding events, next to each element in the list of binding click event, browser. When the user clicks the runtime. SendNativeMessage sending messages, and access to close the window after the return value.

The code looks something like this:

item.addEventListener('click'.async function(){
    await browser.runtime.sendNativeMessage(
        'native_launcher'{},// The data passed to the native application named native_launcher
    );
    window.close();
});
Copy the code

It is important not to call window.close() too soon to close the pop-up layer, otherwise the pop-up layer script will be closed too soon and will not receive the response from the native application. The Browser Toolbox usually throws an NS_ERROR_NOT_INITIALIZED or can’t access dead object error.

That’s pretty much it for the browser-side extension, and then the native_Launcher native application configuration and writing in the code.

Native Application Configuration

Configuration includes two points, manifest configuration and registry (Windows) configuration, about configuration, MDN native application manifest has been described in great detail.

The following is a list of Native Messaging manifests for Firefox Shortcuts:

{
    "name": "native_launcher"."description": "Launch the native app of you computer."."path": "target/debug/native_launcher.exe"."type": "stdio"."allowed_extensions": [ "[email protected]"]}Copy the code

Native application writing

Here we go!!

The browser side calls up the native application and sends and receives data through STDIO.

When extending to native applications to send messages via stdin, it will first:

  • Serialize the message to a UTF-8 string
  • Calculates the number of bytes of a string

Data is then sent, including:

  • The number of bytes calculated above is expressed in 4 bytes
  • The message body

It is also important to note that endienment uses native endienment, not big endienment directly.

Each message will be jSON-formatted, which means that if the native application wants to return a string hello, it needs to return a “Hello” message to be jSON-formatted.

The following Rust code contains the following header:

use std::env;
use std::process::Command;
use std::os::windows::process::CommandExt;
use winapi::winbase;
use std::io::{Read,Write,self};
use std::mem::transmute;

#[macro_use] extern crate json;
extern crate winapi;
Copy the code

Here is the function Rust uses to read the string returned by stdin:

// The first four bytes are read to indicate the length of the data
fn get_stdin(len:usize) - >String{
    let mut stdin =io::stdin();

    let mut stdin_len:u32;
    if len==0 {
        let mut stdin_len_byte =[0u8;4];
        stdin.read_exact(&mut stdin_len_byte).unwrap();
        stdin_len =unsafe{transmute(stdin_len_byte)};
    } else {
        stdin_len =len as u32;
    };

    let mut byte =[0u8;1];
    let mut data :Vec<u8> =Vec::new();

    while stdin_len >0 {
        stdin_len =stdin_len-1;
        io::stdin().read_exact(&mut byte).unwrap();
        for elt in byte.iter() {
            data.push(*elt);
        };
    };

    return String::from_utf8(data).unwrap();
}
Copy the code

When returning a message, the same rules apply to receiving the message, which outputs four bytes in STdout to represent the length of the body bytes before the body itself.

Here is the function for Rust to accept string output to stdout:

fn send_stdout(message:&str) {let mut stdout =io::stdout();

    let message_length =message.len() as u32;
    let message_length_bytes: [u8; 4] = unsafe{transmute(message_length)};

    stdout.write(&message_length_bytes).unwrap();
    stdout.write(message.as_bytes()).unwrap();

    stdout.flush().unwrap();
}
Copy the code

That’s not the end!

In Windows, if you want to keep child processes after the main process exits, you need to pass a CREATE_BREAKAWAY_FROM_JOB flag when the program starts.

Command::new(windows_path+app_path)
    .creation_flags(winbase::CREATE_BREAKAWAY_FROM_JOB)
    .args(&app_args)
    .spawn()
    .expect("failed to execute child");Copy the code

But the runtime failed:

Error {repr: Os {code: 5, message: "Access denied." }}Copy the code

CREATE_BREAKAWAY_FROM_JOB CREATE_BREAKAWAY_FROM_JOB

The child processes of a process associated with a job are not associated with the job.

If the calling process is not associated with a job, this constant has no effect. If the calling process is associated with a job, the job must set the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit.

That is, JOB_OBJECT_LIMIT_BREAKAWAY_OK must be passed to the process before CREATE_BREAKAWAY_FROM_JOB can be passed to the invoked child process during execution.

JOB_OBJECT_LIMIT_BREAKAWAY_OK: JOB_OBJECT_LIMIT_BREAKAWAY_OK

If the native application is launched directly with the extension call sendNativeMessage, there is the JOB_OBJECT_LIMIT_BREAKAWAY_OK flag, but what about testing?

Here’s what I did. Start a CMD and call the compiled.exe directly from CMD. In Rust’s case, the command to compile is cargo Build, and the command to compile and run is cargo Run. Therefore, from the original:

cargo run
Copy the code

Becomes:

cargo build && .\target\debug\native_launcher.exe
Copy the code

Native_launch.exe is called with the JOB_OBJECT_LIMIT_BREAKAWAY_OK flag.

Afterword.

Thus, a new version of Firefox shortcut was developed.

The code for the Firefox shortcut used in this article can be found here:

  • pea3nut/some-extension-components-for-firefox

Finally, the slogan:

Mozilla, a global community working together to keep the Web open, public and accessible to all.

Mozilla is a global community working together to keep the Internet open, public, and available to all.