chrome extension with react

Previous blog post was about manipulating images with Javascript API and here is a recap of my notes after creating a new Chrome extension app available here.

Goal

Create a chrome extension to fetch and download all displayed (or not) images from current web page.

Stack

100% Javascript with ES6 sugar syntax and React library.

My first try of creating a chrome prototype app few years ago was done with a popular framework you may have known called AngularJS is still available for free :)

Then I had to choose the right tool to bundle appplication. Gulp, Grunt, Browserify, Webpack, Systemjs... javascript fatigue ? @Vjeux has already pointed out that is quite difficult today to start a new JS project quickly

I had already used webpack, grunt or gulp and a try with browserify which was really nice so it was my end choice combined with awesome watchify tool.

Development

Before to start developing your own chrome extension a nice overview is given here and here.

Then you might fork any of these provided samples from this Google Github Repository. Off course you will have to clean it, rename, remove files...boring I know :)

Ok now you are ready.

Keep in mind that React documentation is always useful and may changes with version updates.

Browserify and watchify

Browserify bundles your javascript files while watchify triggers this processus to be run in background when updates occured in your source files.

Installation is an npm standard:

npm i -g browserify watchify

Basic watchify command could be

watchify main.js -o static/bundle.js

Mine

watchify --debug -t [ babelify --presets [ es2015 react ] --plugins 
[transform-object-rest-spread] ] src/app/app.js -o extension/bundle.js

This way, you will got all Babel transpiler features available depending on your specific ES6 sugar needs combined with React library. See my package.json for dependencies check.

Common errors

Maybe some of you will one day get this warning.

Error: Parsing file app.js: 'import' and 'export' may appear only 
with 'sourceType: module'

Don't panic, that was just a missing parameter with es2015 babel preset :

> watchify --debug -t [ babelify --presets [ react ] ] ...

to

> watchify --debug -t [ babelify --presets [ es2015 react ] ] ...

Small bash script here

Content script

Mainly what you need to keep in mind is that a chrome extension can interact with your current page, read its content, update it...by injection of some "content javascript file" as described here

"Extensions can interact with web pages or servers using content scripts or cross-origin XMLHttpRequests. Extensions can also interact programmatically with browser features such as bookmarks and tabs."

Console

Messaging system

Event driven chrome extension API is quite simple and permit bidirectional communication between extension and contentscript side.

Send

sendMessage(tabId, request, callback)

details

Receive

onMessage.addListener(callback);

details

See below for a listener callback example.

It is very intuitive and you can query to get current chrome tab or others by instance.

Sent messages use JSON format, so everything is serialized this way, my choice was to deeply use base64 data encoding system. A process of deserialization is done each time a message is captured by an event listener on content script side of extension side (parseJSON ?), heavy task ?....

Be careful with messaging system and synchronous or asynchronous task.

In case of asynchronous task, just remember to return true in your event listener handler function.

Synchronous receiver

(request, sender, sendResponse) => {
  // doSomething synchronous
  doSync();
  // sendResponse is optional but useful to send back to origin
  sendResponse({data: 'this was synchronous'});
  // return false by default
}

ASynchronous receiver

(request, sender, sendResponse) => {
  // doSomething synchronous, Promise, Async, Callback... 
  doASync( 
    (data) => {
      sendResponse({data: 'this was asynchronous'});
    }
  );    
  return true; // MANDATORY
}

Chrome API More

Conclusion

Dealing with chrome API is simple but debugging it ( both extension/contentscript side) into browser, dev consoles, is sometimes annoying because you need to activate extension to get chrome context, reload you extension manually often from here chrome://extensions/

I was thinking about to mock chrome API in order to fully concentrate on my code, but it looks like guys have already done it, I will give a try later

Code is available on this repository

https://github.com/acvetkov/sinon-chrome https://github.com/sethmcl/chrome-mock

Just for fun


Tags: Angular Chrome Javascript React