Posts Tagged ‘firefox’

js-ctypes: from C to JavaScript to C

Monday, October 13th, 2014

Contact ‘mar10 [a] krutt [punto] org’

In this tutorial I will show how to manipulate C data types:
numbers, chars, structures, pointers and c-strings in JavaScript.

I am assuming the reader is familiar with the Firefox Add-on SDK, otherwise it’s time to read this tutorial and keep in mind the jsctypes api.

You can download all the code here. Please READ the README and LICENSE txt files before to use the code.

Requirements:

  • A working gcc compiler. I am using gcc 4.8.2
  • A text editor
  • Firefox. I am using Firefox 32.0.3
  • The Add-ons SDK installed. I am using Add-on SDK 1.17

Step 1: The C language: structs, pointers, c-strings, …

We define the following struct:

struct st_t
{
  void   *self;
  char   *str;
  size_t  buff_size;
  int     i;
  float   f;
  char    c;
};

The self pointer is just a pointer to the memory block that hosts the struct. We also have a pointer to char that will hold a c-string. The size of the memory block pointed to by str is stored in buff_size. Remember that in C language, size_t is a data type that is machine dependent. It doesn’t have the same size in a 32 bit machine than in a 64 bit computer.

Access functions

In order to use our previously defined struct, we declare the following functions:

struct st_t *f_st_rand();
void f_st_free( struct st_t *st );
int f_st_save( struct st_t *st);

*f_st_rand() just allocates memory for a struct st_t and initializes the struct.

*f_st_free( struct st_t *st ) releases the memory block pointed to by st and by st->str.

*f_st_save( struct st_t *st) stores in a file named output.txt the values of the struct.

You must compile a shared object and install the .so file in your system. Otherwise, Firefox will be unable to locate the library.
See this tutorial for more details.

In our setting, the created shared object is libexample2.so.

Step 2: js-ctypes

In our main.js file we need to do this:

  1. Load the shared object libexample2.so
  2. Declare a data type that matches the struct st_t
  3. Declare a pointer to the previously defined JavaScript struct
  4. Declare functions that match with the c-functions listed above
  5. Operate the data received from the C library
  6. Call the access functions from JavaScript code

1. Loading ‘libexample2.so’

Easy:

var libexample2 = ctypes.open("libexample2.so");

2. Declaring a struct in JavaScript

We call the function ctypes.StructType in order to define a new JavaScript struct. To define a pointer we use: ctypes.PointerType( ... ). The argument must be a ctype that matches the type declared in the struct.

The code snippet for this task is listed below.

/*
 *   struct st_t
 *   {
 *     void   *self;
 *     char   *str;
 *     size_t  buff_size;
 *     int     i;
 *     float   f;
 *     char    c;
 *   };
 */

var st_t = new ctypes.StructType("st_t",
                      [ { "self": ctypes.PointerType(ctypes.void_t) },
                        { "str": ctypes.PointerType(ctypes.char) },
                        { "buff_size": ctypes.size_t },
                        { "i": ctypes.int },
                        { "f": ctypes.float },
                        { "c": ctypes.char } ]);

3. Pointers to structs in JavaScript

Easy:

var st_ptr_t = ctypes.PointerType(st_t);

4. JavaSctipt functions

We need to use our previously defined variable libexample2 and the method declare:

var f_st_rand = libexample2.declare("f_st_rand",     /* function name  */
                              ctypes.default_abi,    /* call ABI       */
                              st_ptr_t);             /* return type    */

var f_st_free = libexample2.declare("f_st_free",     /* function name  */
                              ctypes.default_abi,    /* call ABI       */
                              ctypes.void_t,         /* return type    */
                              st_ptr_t);             /* fcn's argument */

var f_st_save = libexample2.declare("f_st_save",     /* function name  */
                              ctypes.default_abi,    /* call ABI       */
                              ctypes.int,            /* return type    */
                              st_ptr_t );            /* fcn's argument */

5. Modifying C variables in JavaScript

Primitive C-data-types are handled in a similar way that JavaScript types. However for pointers, c-strings and similar stuff we must act very carefully.

C-strings in JavaScript

We need to do a trick in JavaScript in order to use a memory block formatted for a c-string. The JavaScript pointer must be casted to another JS pointer with the same type but specific size. Memory blocks coming from C code don’t carry that information.

 var ptr = ctypes.cast( st.str, ctypes.ArrayType( ctypes.char, st.buff_size ).ptr );

We use an ctypes.char array of specific size (the same size of our buffer). The property ptr of this array is used to get a pointer.

Assigning a JavaScript string to a c-string

Easy:

ptr.contents = String("Hello world from JavaScript!!!");

6. Calling the c-functions from JavaScript

We just use our JavaScript functions:

var st_ptr = f_st_rand();
f_st_save( st_ptr ); 
f_st_free( st_ptr );

Final remarks

Once the code is ready we create the xpi file and we install it in Firefox via the Add-on manager.

It’s recommended that you open the Firefox Console during the execution of this add-on in order to catch any message coming from the add-on.

See also

License

Copyright (C) 2014 mar10

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

A copy of the license can be found here

Loading a native library in Firefox via the Add-ons SDK

Thursday, October 2nd, 2014

Contact: mar10 a krutt punto org

In this tutorial we are going to discover how to use a small c library written by ourselves inside Firefox.

This tutorial assumes that you have a working GNU/Linux distribution with a gcc environment installed. Here we use Ubuntu, however all the steps must work for any POSIX system in which the Add-ons SDK runs.

Requirements:

  • A working gcc compiler. I am using gcc 4.8.2
  • A text editor
  • Firefox. I am using Firefox 32.0.3
  • The Add-ons SDK installed. I am using Add-on SDK 1.17

Please go to Mozilla Add-ons SDK to get familiar with the SDK and specially with the cfx tool.

We could use any system library (shared object) in order to demonstrate how to use the Add-ons SDK. However I prefer the long way. We start by coding our own c-library. After that, we will start with the add-on.

Step 1: Preparing the directories

Open a terminal, and create a working directory, i.e. do something like:

mkdir example example/c example/add-on

After that, you would have the following directories:

.
|- example
    |-- add-on
    |-- c

Step 2: Creating the library

We need to write some C code in order to produce a shared object. Copy the following to a file named example.c inside the c directory:

#include <stdio.h>

int sum( int a, int b )
{
  int c;
  FILE *f;

  c = a+b;

  f = fopen("output.txt", "a");
  fprintf(f, "%d + %d = %d\n", a, b, c);
  fclose(f);

  return c;
}

This code just prints the sum of two numbers. Each time this function is called, it will append a new line to file output.txt.

Compile it with the following commands:

gcc -Wall -Wextra -std=c89 --pedantic -fPIC -c example.c -o example.o
gcc -shared example.o -o libexample.so

After that, you will have something like this:

.
|-- example
    |-- add-on
    |-- c
        |-- example.c
        |-- example.o
        |-- libexample.so

From a terminal at the example directory, run the following:

file c/libexample.so

you would see this:

c/libexample.so: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), 
dynamically linked, BuildID[sha1]=..., not stripped

Note that now we have a shared object (libexample.so) that must be installed in the target system in order to be consumed by Firefox.

Step 3: Installing the library

In order to use the library in our target system, we must install it. Assuming that you are at the example directory, do this:

sudo cp c/libexample.so /usr/lib
sudo chmod 0755 /usr/lib/libexample.so

If everything went fine, you must have libexample.so in /usr/lib. Now do this:

sudo ldconfig
ldconfig -p | grep example

You must see something like this:

libexample.so (libc6,x86-64) => /usr/lib/libexample.so

This means that you have successfully installed libexample.so, and that your system will be able to load libexample.so when asked by an application.

Step 4: Testing the library

Create a file named example/c/main.c with the following content:

#include <stdio.h>

int sum( int a, int b );

int main()
{
  int ret;
  ret = sum(1, 2);
  printf("1 + 2 = %d\n", ret);
  return 0;
}

Go to example/c. Compile main.c, as follows:

gcc -Wall -Wextra -std=c89 --pedantic -c main.c -o main.o

And now, link the object file (main.o) with the library:

gcc main.o -lexample -o main

Use the ldd command to review the new executable file:

ldd main

You must see something like this:

linux-vdso.so.1 =>  (0x00007fff02fc1000)
libexample.so => /usr/lib/libexample.so (0x00007f4c82181000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4c81dbb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4c823a8000)

Run the executable:

./main

It must print 1 + 2 = 3.

Step 5: Creating the add-on

Here we make use of the Add-ons SDK. Go to the add-on directory and run:

cfx init

This command will create a template for the add-on. Your add-on directory now have the following structure:

.
|-- example
    |-- add-on
    |   |-- data
    |   |-- lib
    |   |   |-- main.js
    |   |-- package.json
    |   |-- test
    |       |-- test-main.js
    |-- c
        |-- example.c
        |-- example.o
        |-- libexample.so
        |-- main
        |-- main.c
        |-- main.o

Customizing packaje.json

Open the packaje.json file, you will see this:

{
  "name": "add-on",
  "title": "add-on",
  "id": "jid1-k4igzLTBAsNAew",
  "description": "a basic add-on",
  "author": "",
  "license": "MPL 2.0",
  "version": "0.1"
}

You can change the information contained in packaje.json. These fields will be visible inside the Firefox’s add-ons manager.

For example, you could put this in the packaje.json file:

{
  "name": "mar10_add-on",
  "title": "add-on example",
  "id": "jid1-k4igzLTBAsNAew",
  "description": "a basic add-on",
  "author": "mar10",
  "license": "MPL 2.0",
  "version": "0.1a"
}

The value of the "name" field will be used by the cfx tool to name the xpi add-on file.

Open the main.js file and copy this:

/* import js-ctypes */
var {Cu} = require("chrome");
var {ctypes} = Cu.import("resource://gre/modules/ctypes.jsm", null);

/* Open the library */
try 
{
  var libexample = ctypes.open("libexample.so");

  /* 
   * remember that our c-function is of the form:
   * int sum( int a, int b )
   */

  var sum = libexample.declare("sum",              /* function name */
                               ctypes.default_abi, /* call ABI */
                               ctypes.int,         /* return type */
                               ctypes.int,         /* argument type */
                               ctypes.int);        /* argument type */
  var num_a = 1;
  var num_b = 2;
  var ret = sum( num_a, num_b);
  console.log("Result: " + num_a + " + " + num_b + " = " + ret );
  libexample.close();
} 
catch (e) 
{
  console.log("Error while loading lib: " + e);
}

Run the following command:

cfx run

You will see something like this:

console.log: mar10_add-on: Result: 1 + 2 = 3

Perhaps you will find more information coming from Firefox, i.e. some GLib-GObject warnings. Don’t pay attention to those messages.

At the same directory, you will find the output.txt file with the same information you just saw at the terminal!

To create the xpi file, run the following command at the example/add-on directory:

cfx xpi

This creates the mar10_add-on.xpi file. Now you can import this add-on to Firefox.

Step 6: Importing the add-on to Firefox

Just press Ctrl+O to launch the open file dialog. Select the xpi file and
click open. In my case the add-on is mar10_add-on.xpi.
A new window will appear. Click Install Now.

Install Add-on

Go to the Add-ons manager and click on Extensions:

Install Add-on

Now click on More over our recently installed add-on, you will see something like this:

Install Add-on

Once the add-on is installed, our library libexample.so is executed each time Firefox is initiated, inclusive at the moment the add-on was installed!!!

Now write the following command:

cat output.txt

The content of output.txt must be: 1 + 2 = 3, awesome!!!. Each time you start Firefox, you will append a new line with the same content to output.txt.

Step 7: Running Firefox

Run Firefox from a terminal:

/usr/bin/firefox

The output.txt file will be created inside the directory that you use to call Firefox.

If you run Firefox by clicking over an icon in the desktop or via another graphic element, the output.txt file will be created at $HOME directory.

Final remarks

As you can see, this add-on is almost useless. There is no interaction with the browser, or with the user. However, now you are able to compile and load native libraries in Firefox!!!

See also

Download

Here you can get the code of this tutorial.

License

Copyright (C) 2014 mar10

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

A copy of the license can be found here.