Building Chrome Extensions using Ember CLI

 

Nick Blanchet  @h55nick

André Malan    @ramcio

SimpleReach

http://simplereach.com

Dear God Why?

  • Why climb Everest?
  • Small app for teaching Ember
  • It's not that hard and Ember is awesome

What we built...

Building a Chrome Extension

  • Manifest File
  • Background JS
  • Developer Pane Include

Manifest.json

{
  "name": "SimpleReach Internal Extension",
  "version": "2.5",
  "manifest_version": 2,
  "description": "Internal use tool for admin purposes",
  "icons": { "128": "icon128.png" },
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "dist/index.html"
  },
  "devtools_page": "html/devtools.html",
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
  "background": {
    "page": "html/background.html"
  },
  "omnibox": { "keyword" : "SR" },
  "permissions": [
    "background",
    "webRequest",
    "debugger",
    "tabs",
    "http://*/*",
    "https://*/*"
  ]
}

background.js

//
// Add webRequest Listener
//
console.log("[SRPLUGIN] Add Listener to edge.simplereach.com");
chrome.webRequest.onBeforeRequest.addListener(
  addRequestToLocalStorage, { urls: [ '*://edge.simplereach.com/*' ] }
);

//
// Properly format data to insert into localstorage
//
function addRequestToLocalStorage(details){
   var result = SPR.decode(details.url.split('?')[1]);
   result.type = details.url.split('?')[0].split('/')[3];

   result.id =  details.method + ':' + details.requestId;
   result.ts =  details.timeStamp;
   result.tab_id =  details.tabId;

   // add to DB
   BG.addRequestToLocalStorage(result);
};

Our Server

devtools.html

<!doctype html>
<html>
  <head>
    <style>
      body {
        min-width: 357px;
        overflow-x: hidden;
      }
      img {
        margin: 5px;
        border: 2px solid black;
        vertical-align: middle;
        width: 75px;
        height: 75px;
      }
    </style>
    <script src="devtools.js"></script>
  </head>
  <body>
  </body>
</html>
var panelWindow,
    injectedPanel = false,
    injectedPage = false,
    panelVisible = false,
    savedStack = [];
chrome.devtools.panels.create(
  "Reach", "/icon.png", "/dist/index.html"
);

Making it work with Ember CLI

ember build --watch

Security for evals

//manifest.json
{
    "content_security_policy": "default-src 'self' 'unsafe-eval'",
}

Debugging

Dit maak jou gat seer...

Run as plain Ember app (also good for testing)

//vendor/chrome/chrome.js

if(!chrome.devtools){
  console.log('loading DEV CHROME');
  chrome.devChrome = true;
  chrome.devtools = {
    inspectedWindow: {
      eval: function(evalString){ },
      getResources: function(callback){ },
      reload: function(options){ }
    }
  });
}
//brocfile.js

app.import('vendor/chrome/chrome.js');

Model Layer

(because everybody loves Ember Data)

LocalStorage Adapter

import DS from 'ember-data';

export default DS.LSAdapter.extend({
  namespace: 'reach'
});

https://github.com/kurko/ember-localstorage-adapter

  startServices: function(){
    var self = this;
    function onStorageEvent(storageEvent){
      if(storageEvent['key'] === 'reach'){
        self.store.find('n').then(function(views){
          self.set('model', views);
        });
      }
    }
    window.addEventListener('storage', onStorageEvent, false);
  }.on('init')
BG.addRequestToLocalStorage = function(responseObject){
  var localStorageJSON = JSON.parse(localStorage.getItem(DB.localStorageName));
  if (!localStorageJSON){ // create proper structure for localStorage DB.
    localStorageJSON = {
      'n' : {'records': {}},
      't' : {'records': {}},
      'x' : {'records': {}}
    };
  }

  localStorageJSON[responseObject.type]['records'][responseObject.id] = responseObject;
  localStorage.setItem(DB.localStorageName, JSON.stringify(localStorageJSON));
};

Custom Chrome Adapter

https://github.com/emberjs/ember-inspector/tree/master/app/adapters


/* globals chrome */
import BasicAdapter from "./basic";

var emberDebug = null;

export default  BasicAdapter.extend({
  name: 'chrome',

  sendMessage: function(options) {
    options = options || {};
    this.get('_chromePort').postMessage(options);
  },

  _chromePort: function() {
    return chrome.extension.connect();
  }.property(),

  _connect: function() {
    var self = this;
    var chromePort = this.get('_chromePort');
    chromePort.postMessage({ appId: chrome.devtools.inspectedWindow.tabId });

    chromePort.onMessage.addListener(function(message) {
      if (typeof message.type === 'string' && message.type === 'iframes') {
        sendIframes(message.urls);
      }
      self._messageReceived(message);
    });
  }.on('init'),

Fin!

Questions?

Thanks to @pnts for helping with styles

Thanks to Ember CLI folks

Thanks to Ember Inspector folks for lots of code examples

 

http://www.simplereach.com/careers

Ember Chrome Extensions

By Andre Malan

Ember Chrome Extensions

  • 2,042