SlideShare a Scribd company logo
Browser Extensions




  Erwan Loisant <elo@zenexity.com>

             @erwan
Why?

• Integrate deeply with a service
• Add new features to the browser
• Implement a recent spec in a lagging
  browser
• Anything you can think of!
History

• 20th Century: plugins, native addons
• 2002: Mozilla 1.0 written in XUL and Javascript
• 2004: Firefox 1.0 with a focus on extensions
• 2007: FUEL (Firefox User Extension Library)
• 2009: Jetpack, Chrome Extensions
POWER
     vs




EASE OF DEV
Paris js extensions
local storage
                   Javascript
            JSON

CSS                     HTML
Paris js extensions
{
    "name": "Telify",
    "version": "0.1",
    "description": "Click to call",
    "background_page": "background.html",
    "page_action": {
        "default_icon": "icons/tel.gif",
        "default_popup": "popup.html"
    },
    "content_scripts" : [{
        "matches" : ["http://*/*"],
        "js" : ["contentScript.js"]
    }],
    "permissions": [
        "http://cdnjs.cloudflare.com"
    ]
}




              manifest.json
var phoneRe = /^s*((d{2}s?){5})s*$/;

var texts = document.evaluate(".//text()[normalize-space(.)!
='']", document.body, null, 6, null);

var phones = [];
for (var i = 0; i < texts.snapshotLength; i++) {
    var textNode = texts.snapshotItem(i);
    var text = textNode.textContent;
    var m = phoneRe.exec(text);
    if (m != null) {
        var newText = document.createElement("a");
        newText.setAttribute("href", "callto:" + m[1]);
        newText.appendChild(document.createTextNode(m[0]));
        textNode.parentNode.replaceChild(newText, textNode);
        phones.push(m[1]);
    }
}

if (phones.length > 0) {
    chrome.extension.sendRequest(
        {"phones": phones},
        function(response) {});
}

                     contentScript.js
<script>

// Global variable accessed by the popup
var telList;

chrome.extension.onRequest.addListener(function(request,
sender, sendResponse) {
    telList = request["phones"];
    chrome.pageAction.show(sender.tab.id);
    sendResponse({});
});

</script>




                  background.html
<!DOCTYPE html>

<html>
  <style> body { width: 200px; } </style>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/
zepto/0.7/zepto.min.js"></script>
  <script>
    $(document).ready(function(){
       var telList =
chrome.extension.getBackgroundPage().telList;
       if (telList && telList.length > 0) {
         for (i in telList)
           $("ul").append("<li><a href='callto:" +
telList[i] + "'>" + telList[i] + "</a></li>");
       }
    });
    </script>

    <body><ul></ul></body>
</html>




                     popup.html
Paris js extensions
Paris js extensions
XPCOM

      XUL
                  Javascript
            XBL

CSS                        HTML
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css"
  href="chrome://custombutton/content/button.css"?>

<!DOCTYPE overlay >
<overlay id="custombutton-overlay"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul">

<script type="application/javascript"
  src="chrome://custombutton/content/button.js"/>

<!-- Firefox -->
<toolbarpalette id="BrowserToolbarPalette">
  <toolbarbutton id="custom-button-1"/>
  </toolbarpalette>

<!-- button details -->
<toolbarbutton id="custom-button-1"
  label="Custom"
  tooltiptext="My custom toolbar button"
  oncommand="CustomButton()"
  class="toolbarbutton-1 chromeclass-toolbar-additional custombutton"
  />

</overlay>

                             overlay.xul
function installButton(toolbarId, id, afterId) {
    if (!document.getElementById(id)) {
        var toolbar = document.getElementById(toolbarId);

        var before = toolbar.firstChild;
        if (afterId) {
            let elem = document.getElementById(afterId);
            if (elem && elem.parentNode == toolbar)
                before = elem.nextElementSibling;
        }

        toolbar.insertItem(id, before);
        toolbar.setAttribute("currentset", toolbar.currentSet);
        document.persist(toolbar.id, "currentset");

        if (toolbarId == "addon-bar")
            toolbar.collapsed = false;
    }
}

if (firstRun) {
    installButton("nav-bar", "my-extension-navbar-button", "urlbar");
    installButton("addon-bar", "my-extension-addon-bar-button");
}



                            overlay.xul
AKA:
“The Chrome way applied to Firefox”
{
    "name": "weather-example",
    "license": "MPL 1.1/GPL 2.0/LGPL 2.1",
    "author": "Alexandre Poirot",
    "version": "1.0",
    "fullName": "Weather example",
    "id": "weather-example",
    "description": "a basic add-on that display a
weather widget"
}




                     package.json
const   data = require("self").data;
const   ss = require("simple-storage");
const   Request = require("request").Request;
const   timer = require("timer");

const panel = require("panel").Panel({
  width: 240,
  height: 180,
  contentURL: data.url("panel.html"),
  contentScriptFile: [data.url("panel.js")],
  onShow: function () {
    this.port.emit("show");
  }
});

[...]




                      lib/main.js
Paris js extensions
Paris js extensions
COM
            C#



C++                 ActiveX



      Windows API
STDMETHODIMP CExplorerBar::SetSite(IUnknown *punkSite) {
    //If a site is being held, release it.
    if(m_pSite) {
        m_pSite->Release();
        m_pSite = NULL;
    }

    //If punkSite is not NULL, a new site is being set.
    if (punkSite) {
        //Get the parent window.
        IOleWindow *pOleWindow;

        m_hwndParent = NULL;

        if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow,
                     (LPVOID*)&pOleWindow))) {
            pOleWindow->GetWindow(&m_hwndParent);
            pOleWindow->Release();
        }

        if(!m_hwndParent) return E_FAIL;

        if(!RegisterAndCreateWindow()) return E_FAIL;

        //Get and keep the IInputObjectSite pointer.
        if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite,
                     (LPVOID*)&m_pSite))) {
            return S_OK;
        }

        return E_FAIL;
    }

    return S_OK;
}
Fortunately, there’s more...
Accelerators (IE8+)
Pinning API (IE9+)
window.external.msSiteModeClearIconOverlay()
window.external.msSiteModeSetIconOverlay()
window.external.msSiteModeActivate()
window.external.msIsSiteMode()
Do we really need
  extensions?
Do we really need
  extensions?
last word: don’t abuse
      extensions!
Questions?

    @erwan
elo@zenexity.com

More Related Content

What's hot (20)

PDF
MongoDB全機能解説2
Takahiro Inoue
 
PDF
MongoDBで作るソーシャルデータ新解析基盤
Takahiro Inoue
 
PDF
HTML,CSS Next
지수 윤
 
KEY
Perl Web Client
Flavio Poletti
 
PDF
Testing Web Applications with GEB
Howard Lewis Ship
 
PDF
Nubilus Perl
Flavio Poletti
 
PDF
MongoDB Oplog入門
Takahiro Inoue
 
DOC
Php
Linh Tran
 
PDF
PhoneGap: Local Storage
Ivano Malavolta
 
PDF
PuppetDB, Puppet Explorer and puppetdbquery
Puppet
 
PDF
Using web2py's DAL in other projects or frameworks
Bruno Rocha
 
PDF
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
PDF
Ember background basics
Philipp Fehre
 
PDF
Web2py
Lucas D
 
PDF
Remy Sharp The DOM scripting toolkit jQuery
deimos
 
PPTX
Super Advanced Python –act1
Ke Wei Louis
 
PDF
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Jung Kim
 
PDF
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
MongoSF
 
PPT
Praktik Pengembangan Konten E-Learning HTML5 Sederhana
Muhammad Yusuf
 
MongoDB全機能解説2
Takahiro Inoue
 
MongoDBで作るソーシャルデータ新解析基盤
Takahiro Inoue
 
HTML,CSS Next
지수 윤
 
Perl Web Client
Flavio Poletti
 
Testing Web Applications with GEB
Howard Lewis Ship
 
Nubilus Perl
Flavio Poletti
 
MongoDB Oplog入門
Takahiro Inoue
 
PhoneGap: Local Storage
Ivano Malavolta
 
PuppetDB, Puppet Explorer and puppetdbquery
Puppet
 
Using web2py's DAL in other projects or frameworks
Bruno Rocha
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Tsuyoshi Yamamoto
 
Ember background basics
Philipp Fehre
 
Web2py
Lucas D
 
Remy Sharp The DOM scripting toolkit jQuery
deimos
 
Super Advanced Python –act1
Ke Wei Louis
 
Letswift18 워크숍#1 스위프트 클린코드와 코드리뷰
Jung Kim
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
MongoSF
 
Praktik Pengembangan Konten E-Learning HTML5 Sederhana
Muhammad Yusuf
 

Similar to Paris js extensions (20)

KEY
Html5 For Jjugccc2009fall
Shumpei Shiraishi
 
PPT
Play!ng with scala
Siarzh Miadzvedzeu
 
PDF
mobl
zefhemel
 
PPTX
Html5 and web technology update
Doug Domeny
 
PDF
JavaScript APIs - The Web is the Platform
Robert Nyman
 
KEY
前端概述
Ethan Zhang
 
PDF
Is HTML5 Ready? (workshop)
Remy Sharp
 
PDF
Is html5-ready-workshop-110727181512-phpapp02
PL dream
 
PDF
Mozilla Web Apps - Super-VanJS
Robert Nyman
 
PPTX
Python Code Camp for Professionals 3/4
DEVCON
 
PPTX
Spicy javascript: Create your first Chrome extension for web analytics QA
Alban Gérôme
 
PDF
Micro app-framework
Michael Dawson
 
PDF
Micro app-framework - NodeLive Boston
Michael Dawson
 
PDF
Backbone.js — Introduction to client-side JavaScript MVC
pootsbook
 
PPTX
KISSY 的昨天、今天与明天
tblanlan
 
PDF
mobl presentation @ IHomer
zefhemel
 
KEY
Week 4 - jQuery + Ajax
baygross
 
PDF
kissy-past-now-future
yiming he
 
PDF
HTML5 APIs - Where No Man Has Gone Before! - GothamJS
Robert Nyman
 
PDF
HTML5 APIs - Where no man has gone before! - Altran
Robert Nyman
 
Html5 For Jjugccc2009fall
Shumpei Shiraishi
 
Play!ng with scala
Siarzh Miadzvedzeu
 
mobl
zefhemel
 
Html5 and web technology update
Doug Domeny
 
JavaScript APIs - The Web is the Platform
Robert Nyman
 
前端概述
Ethan Zhang
 
Is HTML5 Ready? (workshop)
Remy Sharp
 
Is html5-ready-workshop-110727181512-phpapp02
PL dream
 
Mozilla Web Apps - Super-VanJS
Robert Nyman
 
Python Code Camp for Professionals 3/4
DEVCON
 
Spicy javascript: Create your first Chrome extension for web analytics QA
Alban Gérôme
 
Micro app-framework
Michael Dawson
 
Micro app-framework - NodeLive Boston
Michael Dawson
 
Backbone.js — Introduction to client-side JavaScript MVC
pootsbook
 
KISSY 的昨天、今天与明天
tblanlan
 
mobl presentation @ IHomer
zefhemel
 
Week 4 - jQuery + Ajax
baygross
 
kissy-past-now-future
yiming he
 
HTML5 APIs - Where No Man Has Gone Before! - GothamJS
Robert Nyman
 
HTML5 APIs - Where no man has gone before! - Altran
Robert Nyman
 
Ad

Recently uploaded (20)

PDF
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
PDF
introduction to computer hardware and sofeware
chauhanshraddha2007
 
PDF
State-Dependent Conformal Perception Bounds for Neuro-Symbolic Verification
Ivan Ruchkin
 
PDF
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
PPTX
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
PPTX
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PDF
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
PDF
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
PPTX
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
PPTX
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
PPTX
Agile Chennai 18-19 July 2025 | Emerging patterns in Agentic AI by Bharani Su...
AgileNetwork
 
PDF
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
PDF
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
PPTX
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
PPTX
The Future of AI & Machine Learning.pptx
pritsen4700
 
PPTX
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
PDF
Brief History of Internet - Early Days of Internet
sutharharshit158
 
PDF
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
PDF
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
Peak of Data & AI Encore - Real-Time Insights & Scalable Editing with ArcGIS
Safe Software
 
introduction to computer hardware and sofeware
chauhanshraddha2007
 
State-Dependent Conformal Perception Bounds for Neuro-Symbolic Verification
Ivan Ruchkin
 
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
Dev Dives: Automate, test, and deploy in one place—with Unified Developer Exp...
AndreeaTom
 
AI Code Generation Risks (Ramkumar Dilli, CIO, Myridius)
Priyanka Aash
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
Researching The Best Chat SDK Providers in 2025
Ray Fields
 
AI Unleashed - Shaping the Future -Starting Today - AIOUG Yatra 2025 - For Co...
Sandesh Rao
 
cloud computing vai.pptx for the project
vaibhavdobariyal79
 
Introduction to Flutter by Ayush Desai.pptx
ayushdesai204
 
Agile Chennai 18-19 July 2025 | Emerging patterns in Agentic AI by Bharani Su...
AgileNetwork
 
The Future of Mobile Is Context-Aware—Are You Ready?
iProgrammer Solutions Private Limited
 
Responsible AI and AI Ethics - By Sylvester Ebhonu
Sylvester Ebhonu
 
AVL ( audio, visuals or led ), technology.
Rajeshwri Panchal
 
The Future of AI & Machine Learning.pptx
pritsen4700
 
Applied-Statistics-Mastering-Data-Driven-Decisions.pptx
parmaryashparmaryash
 
Brief History of Internet - Early Days of Internet
sutharharshit158
 
CIFDAQ's Market Wrap : Bears Back in Control?
CIFDAQ
 
Google I/O Extended 2025 Baku - all ppts
HusseinMalikMammadli
 
Ad

Paris js extensions

  • 1. Browser Extensions Erwan Loisant <[email protected]> @erwan
  • 2. Why? • Integrate deeply with a service • Add new features to the browser • Implement a recent spec in a lagging browser • Anything you can think of!
  • 3. History • 20th Century: plugins, native addons • 2002: Mozilla 1.0 written in XUL and Javascript • 2004: Firefox 1.0 with a focus on extensions • 2007: FUEL (Firefox User Extension Library) • 2009: Jetpack, Chrome Extensions
  • 4. POWER vs EASE OF DEV
  • 6. local storage Javascript JSON CSS HTML
  • 8. { "name": "Telify", "version": "0.1", "description": "Click to call", "background_page": "background.html", "page_action": { "default_icon": "icons/tel.gif", "default_popup": "popup.html" }, "content_scripts" : [{ "matches" : ["http://*/*"], "js" : ["contentScript.js"] }], "permissions": [ "http://cdnjs.cloudflare.com" ] } manifest.json
  • 9. var phoneRe = /^s*((d{2}s?){5})s*$/; var texts = document.evaluate(".//text()[normalize-space(.)! ='']", document.body, null, 6, null); var phones = []; for (var i = 0; i < texts.snapshotLength; i++) { var textNode = texts.snapshotItem(i); var text = textNode.textContent; var m = phoneRe.exec(text); if (m != null) { var newText = document.createElement("a"); newText.setAttribute("href", "callto:" + m[1]); newText.appendChild(document.createTextNode(m[0])); textNode.parentNode.replaceChild(newText, textNode); phones.push(m[1]); } } if (phones.length > 0) { chrome.extension.sendRequest( {"phones": phones}, function(response) {}); } contentScript.js
  • 10. <script> // Global variable accessed by the popup var telList; chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { telList = request["phones"]; chrome.pageAction.show(sender.tab.id); sendResponse({}); }); </script> background.html
  • 11. <!DOCTYPE html> <html> <style> body { width: 200px; } </style> <script src="http://cdnjs.cloudflare.com/ajax/libs/ zepto/0.7/zepto.min.js"></script> <script> $(document).ready(function(){ var telList = chrome.extension.getBackgroundPage().telList; if (telList && telList.length > 0) { for (i in telList) $("ul").append("<li><a href='callto:" + telList[i] + "'>" + telList[i] + "</a></li>"); } }); </script> <body><ul></ul></body> </html> popup.html
  • 14. XPCOM XUL Javascript XBL CSS HTML
  • 15. <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/css" href="chrome://custombutton/content/button.css"?> <!DOCTYPE overlay > <overlay id="custombutton-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/ there.is.only.xul"> <script type="application/javascript" src="chrome://custombutton/content/button.js"/> <!-- Firefox --> <toolbarpalette id="BrowserToolbarPalette"> <toolbarbutton id="custom-button-1"/> </toolbarpalette> <!-- button details --> <toolbarbutton id="custom-button-1" label="Custom" tooltiptext="My custom toolbar button" oncommand="CustomButton()" class="toolbarbutton-1 chromeclass-toolbar-additional custombutton" /> </overlay> overlay.xul
  • 16. function installButton(toolbarId, id, afterId) { if (!document.getElementById(id)) { var toolbar = document.getElementById(toolbarId); var before = toolbar.firstChild; if (afterId) { let elem = document.getElementById(afterId); if (elem && elem.parentNode == toolbar) before = elem.nextElementSibling; } toolbar.insertItem(id, before); toolbar.setAttribute("currentset", toolbar.currentSet); document.persist(toolbar.id, "currentset"); if (toolbarId == "addon-bar") toolbar.collapsed = false; } } if (firstRun) { installButton("nav-bar", "my-extension-navbar-button", "urlbar"); installButton("addon-bar", "my-extension-addon-bar-button"); } overlay.xul
  • 17. AKA: “The Chrome way applied to Firefox”
  • 18. { "name": "weather-example", "license": "MPL 1.1/GPL 2.0/LGPL 2.1", "author": "Alexandre Poirot", "version": "1.0", "fullName": "Weather example", "id": "weather-example", "description": "a basic add-on that display a weather widget" } package.json
  • 19. const data = require("self").data; const ss = require("simple-storage"); const Request = require("request").Request; const timer = require("timer"); const panel = require("panel").Panel({ width: 240, height: 180, contentURL: data.url("panel.html"), contentScriptFile: [data.url("panel.js")], onShow: function () { this.port.emit("show"); } }); [...] lib/main.js
  • 22. COM C# C++ ActiveX Windows API
  • 23. STDMETHODIMP CExplorerBar::SetSite(IUnknown *punkSite) { //If a site is being held, release it. if(m_pSite) { m_pSite->Release(); m_pSite = NULL; } //If punkSite is not NULL, a new site is being set. if (punkSite) { //Get the parent window. IOleWindow *pOleWindow; m_hwndParent = NULL; if(SUCCEEDED(punkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow))) { pOleWindow->GetWindow(&m_hwndParent); pOleWindow->Release(); } if(!m_hwndParent) return E_FAIL; if(!RegisterAndCreateWindow()) return E_FAIL; //Get and keep the IInputObjectSite pointer. if(SUCCEEDED(punkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&m_pSite))) { return S_OK; } return E_FAIL; } return S_OK; }
  • 27. Do we really need extensions?
  • 28. Do we really need extensions?
  • 29. last word: don’t abuse extensions!

Editor's Notes

  • #2: * Javascript : dans les pages, dans les serveurs, mais pas seulement !\n
  • #3: * Etendre les fonctionalites du navigateur (Google Gears), fonctionalit&amp;#xE9;s de niche (Chrome to Phone)\n* Fonctionalit&amp;#xE9;s transversales sur toutes les pages\n* Exemple de Spellbound: correction orthographique dans Firefox\n
  • #4: * Mozilla 1.0 bloated =&gt; Firefox 1.0 minimaliste mais avec extensions\n* Forte influence de Mozilla - visionnaire a l&amp;#x2019;epoque du Javascript &amp;#x201C;etoiles qui suivent le curseur&amp;#x201D;\n* Extensions Chrome = Aaron Boodman de Greasemonkey\n
  • #5: * Firefox se base sur la meme plate-forme de d&amp;#xE9;veloppement que les extensions (Firefox lui-meme est &amp;#xE9;crit en Javascript !)\n* Google Chrome a ete developpe principalement par des gens qui viennent du monde Mozilla et ont tire profit de l&amp;#x2019;experience de Firefox\n
  • #6: * Chrome: points d&amp;#x2019;extension clairement definis par une API, seuls points d&amp;#x2019;int&amp;#xE9;gration\n* Empeche des developpeurs de faire n&amp;#x2019;importe quoi (stabilite, performances)\n
  • #7: 100% technos web - tr&amp;#xE8;s facile d&amp;#x2019;acc&amp;#xE8;s pour les d&amp;#xE9;veloppeurs web\n
  • #8: \n
  • #9: \n
  • #10: \n
  • #11: \n
  • #12: \n
  • #13: \n
  • #14: * Firefox: libert&amp;#xE9; totale !\n* API et points d&amp;#x2019;extension existent, mais pas d&amp;#x2019;obligation de se limiter a cela\n* Reecriture de pans entiers du navigateur, desactivation de fonctionalit&amp;#xE9;s\n* Problemes de compatibilite avec les nouvelles version de Firefox\n
  • #15: * Juste javascript, HTML, CSS pour les choses simples\n* XUL langage de balises XML pour d&amp;#xE9;crire les interfaces graphiques, n&amp;#xE9;cessaire pour &amp;#xE9;tendre l&amp;#x2019;interface graphique de Firefox\n* XPCOM pour aller encore plus loin et ajouter des composants natifs\n
  • #16: \n
  • #17: \n
  • #18: * Projet qui existe depuis pres de 3 ans mais reste a l&amp;#x2019;etat de prototype\n* Apporte a Firefox la simplicit&amp;#xE9; de developpement et les extensions installables sans redemarrage\n
  • #19: \n
  • #20: \n
  • #21: \n
  • #22: \n
  • #23: \n
  • #24: \n
  • #25: \n
  • #26: * API Javascript pour ajouter un lien vers un site, avec notifications\n* Positif : fait progresser l&amp;#x2019;interaction application web / desktop\n =&gt; point d&amp;#x2019;evolution majeur des navigateurs aujourd&amp;#x2019;hui\n* IE = gros progres au niveau du support des standards depuis IE8, IE9, IE10\n =&gt; ajoutez un support d&amp;#x2019;extensions en Javascript !\n
  • #27: \n
  • #28: * Les extensions sont commodes mais ont des inconv&amp;#xE9;niants: installation, mise a jour, depend du navigateur\n* Utilisez du pur web quand c&amp;#x2019;est possible, extensions seulement quand necessaire\n
  • #29: \n