{"id":16837,"date":"2017-04-06T12:15:00","date_gmt":"2017-04-06T09:15:00","guid":{"rendered":"https:\/\/www.webcodegeeks.com\/?p=16837"},"modified":"2017-04-05T10:47:14","modified_gmt":"2017-04-05T07:47:14","slug":"webextensions-101","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/","title":{"rendered":"WebExtensions 101"},"content":{"rendered":"<p>If you know the basic web technology stack (HTML, JS, and CSS), then <a href=\"https:\/\/www.webcodegeeks.com\/web-development\/building-web-extensions-can\/\">you can indeed build a WebExtension<\/a>.<\/p>\n<p>It wasn\u2019t always this simple. In the past, one might have needed to know C or Objective-C and the internal workings of the browser\u2019s own code or even the operating system\u2019s idiosyncrasies.<\/p>\n<p>Thankfully, there\u2019s been some recent consolidation efforts and some work toward standardization. If you\u2019re curious about how the actual standards work, be sure to check out the <a href=\"https:\/\/www.w3.org\/community\/browserext\/\">Browser Extension Community Group<\/a> at the <a href=\"https:\/\/www.w3.org\/\">W3C<\/a>.<\/p>\n<p>However, this new collaborative world is still being built. In its current state, there are rough spots and differences (and in-progress polyfills).<\/p>\n<p>Let\u2019s take a look at what it takes today to actually build a WebExtension for real.<\/p>\n<h2>Anatomy of a WebExtension<\/h2>\n<p>In my post <a href=\"https:\/\/www.webcodegeeks.com\/web-development\/building-web-extensions-can\/\">Building WebExtensions Because You Can<\/a>, you\u2019ll find a description of the primary pieces that make up most WebExtensions. We\u2019ll be using just a handful of the available options and focus on getting one up and running.<\/p>\n<p>Here\u2019s the Anatomy of a WebExtension chart from Mozilla for handy reference:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png\"><img decoding=\"async\" class=\"aligncenter wp-image-16839\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png\" alt=\"\" width=\"860\" height=\"860\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png 1200w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-150x150.png 150w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-300x300.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-768x768.png 768w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-1024x1024.png 1024w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-70x70.png 70w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/a><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-16839\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png\" alt=\"\" width=\"1200\" height=\"1200\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy.png 1200w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-150x150.png 150w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-300x300.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-768x768.png 768w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-1024x1024.png 1024w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/webextension-anatomy-70x70.png 70w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/a><\/p>\n<p>Let\u2019s start by installing some basic tooling and setting up the folder structure for this example project.<\/p>\n<h3>Tooling<\/h3>\n<p>To get started, let\u2019s install Mozilla\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/Add-ons\/WebExtensions\/Getting_started_with_web-ext\">web-ext<\/a> command-line tool.<\/p>\n<p>The <code>web-ext<\/code> command makes it possible to run Firefox with an empty profile (no existing cookies, history, or other extensions). This helps when testing your extension as there will be less to get in the way, and command line console debugging output \u2014 if you prefer that.<\/p>\n<p>It also serves as a handy way to package WebExtensions into the necessary <code>.zip<\/code> file and (potentially) communicate with the Mozilla Add-ons site to help you set things for signed self-hosted distribution. The <code>.zip<\/code> file it generates also works with the Chrome and Opera add-on stores.<\/p>\n<p>Let\u2019s use npm to install it as a global command-line tool: <code>sh<br \/>\n$ npm install --global web-ext<\/code><\/p>\n<p>Now. I\u2019m assuming some things here. First, that you have <a href=\"http:\/\/nodejs.org\/\">Node.js<\/a> and <a href=\"http:\/\/npmjs.com\/\">npm<\/a> installed and available (if you\u2019ve got the one, you\u2019ve got the other, btw).<\/p>\n<p>Second, I\u2019ll be setting up the project so that it can be used with <code>npm<\/code> for installing dependencies (and such). However, this is a fairly lightweight project with no need for preprocessing or complex dependencies, so we\u2019ll not be using <code>npm<\/code> directly today. Setting the project up this way though, means you\u2019re ready for it when you need it.<\/p>\n<h3>Organized chaos<\/h3>\n<p>The folder structure below is the one I\u2019ve (mostly) settled on for my WebExtension projects. It provides a foundation for using preprocessors and <code>npm<\/code> for installing dependencies or hooking in all the other <code>node.js<\/code>-style build tooling you might like. Take a look:<\/p>\n<pre class=\"brush:php\">example-webextension\/\r\n+-- extension\/\r\n\u00b6   +-- manifest.json\r\n\u00b6   +-- images\/\r\n\u00b6   |   +-- icon32.png\r\n\u00b6   +-- lib\/\r\n\u00b6   \u00b6   +-- dependency.js\r\n\u00b6   +-- dist\/\r\n\u00b6   \u00b6   +-- nifty.js\r\n\u00b6   +-- popup.js\r\n\u00b6   +-- popup.html\r\n\u00b6   +-- options.html\r\n\u00b6   +-- styles.css\r\n+-- src\/ (optional; for use with typescript, etc)\r\n\u00b6   |   +-- nifty.js (newfangled typescript, etc)\r\n+-- web-ext-artifacts\/\r\n\u00b6   |   +-- example-webextension-0.0.1.zip\r\n+-- package.json\r\n+-- CODE_OF_CONDUCT.md\r\n+-- CONTRIBUTING.md\r\n+-- LICENSE\r\n+-- README.md<\/pre>\n<p>The <code>extension\/<\/code> folder holds the actual \u201coutput\u201d WebExtension. This is the directory that we\u2019ll package up later to distribute to the various WebExtension stores.<\/p>\n<p>Inside this directory is the all-important <code>manifest.json<\/code> file. This file is essentially the first thing read by the browser when installing or running the WebExtension. It sets permissions, picks which scripts to inject in pages, defines where the options interface is located, and many other things.<\/p>\n<p>We\u2019ll dig into that more in a bit, but for now, here\u2019s a look at the other files and folders in that directory:<\/p>\n<ul>\n<li><code>images\/<\/code> \u2013 which, shockingly, contains the images for the extension<\/li>\n<li><code>lib\/<\/code> \u2013 for storing dependencies \u2014 <em>a.k.a.<\/em>, other people\u2019s code<\/li>\n<li><code>dist\/<\/code> \u2013 for any built\/processed output of <em>your<\/em> code<\/li>\n<li><code>*.html<\/code> \u2013 the HTML files used to build the UX for the WebExtension<\/li>\n<li><code>*.css<\/code> \u2013 the CSS file(s) for styling all the things<\/li>\n<\/ul>\n<p>You can certainly reorganize things later if you need, and as your extension grows, this simpler format may not scale to your liking.<\/p>\n<p>It should be noted, that anything in this <code>extension\/<\/code> folder will get included in the distribution package \u2014 even if it\u2019s not used in the WebExtension. This is why we have the extensions contents in a separate folder. For example, we don\u2019t want to accidentally include an entire <code>node_modules\/<\/code> dependency folder in the packaged WebExtension.<\/p>\n<p>The <code>src\/<\/code> directory holds the unprocessed <em>raw<\/em> code for our project. This could be plain old JavaScript, but these days you might be writing in TypeScript or something I\u2019ve not heard of yet. You could also keep your HTML and CSS in this directory if you like preprocessing those from formats like LESS, Jade, etc.<\/p>\n<p>Those, however, aren\u2019t my jam, and I stick with ES6 and (typically) plain old HTML and CSS (as you\u2019ll see below). But you do you, `k?<\/p>\n<p>The <code>web-ext-artifacts\/<\/code> folder (and its contents) are generated by the <code>web-ext<\/code> command-line tool. The <code>.zip<\/code> files here are used when distributing your extension via the various extension stores \u2014 which we\u2019ll touch on at the end.<\/p>\n<p>The <code>package.json<\/code> file is there because I use <code>browserify<\/code> and <code>npm<\/code> to manage the dependencies and build the scripts for (most of) my projects. If you\u2019re using git, then you\u2019ve likely also got a <code>.gitignore<\/code> file that\u2019s excluding <code>node_modules\/<\/code> (or whatever) is holding your dependencies. I\u2019ve ignored them here because I ignore them there \u2014 and you likely already know that bit.<\/p>\n<p>To wrap up the tree, there\u2019s a handful of text or markdown files about important things:<\/p>\n<ul>\n<li>how to contribute to this project: <code>CONTRIBUTING.md<\/code><\/li>\n<li>how to be nice while contributing: <code>CODE_OF_CONDUCT.md<\/code><\/li>\n<li>what permissions you\u2019re giving to others: <code>LICENSE<\/code><\/li>\n<li>what this project is and how to use it: <code>README.md<\/code><\/li>\n<\/ul>\n<p>For most of you, the above list may be terribly introductory. However, it\u2019s very common to find code laying about the internets without two or three of these important files. They\u2019re not all mandatory (per se), but having them will greatly increase your chances of building a successful project with help from other people.<\/p>\n<p>Still with me? Let\u2019s get to work.<\/p>\n<h2>Project Setup<\/h2>\n<p>Let\u2019s go ahead and prep this extension for using <code>npm<\/code> to manage future build tooling, preprocessing, etc. I\u2019m using the name <code>link-extractor<\/code> for this demo extension, but feel free to name yours whatever you\u2019d like.<\/p>\n<p>Open a console and go to your new WebExtension directory. Then run the following command and fill out the questionnaire to your you liking.<\/p>\n<p><code>$ npm init<\/code><\/p>\n<p>Here\u2019s what I filled out (using the defaults for name, entry point, and license; I skipped test and repo for now):<\/p>\n<pre class=\"brush:php\">name: (link-extractor)\r\nversion: (1.0.0) 0.0.1\r\ndescription: Extracts linked data from links.\r\nentry point: (index.js)\r\ntest command:\r\ngit repository:\r\nkeywords: WebExtension, Linked Data, HTML\r\nauthor: BigBlueHat &lt;byoung@bigbluehat.com&gt;\r\nlicense: (ISC)<\/pre>\n<p>Which created this JSON file:<\/p>\n<pre class=\"brush:php\">{\r\n  \"name\": \"link-extractor\",\r\n  \"version\": \"0.0.1\",\r\n  \"description\": \"Extracts linked data from links.\",\r\n  \"main\": \"index.js\",\r\n  \"scripts\": {\r\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\"\r\n  },\r\n  \"keywords\": [\r\n    \"WebExtension\",\r\n    \"Linked\",\r\n    \"Data\",\r\n    \"HTML\"\r\n  ],\r\n  \"author\": \"BigBlueHat &lt;byoung@bigbluehat.com&gt;\",\r\n  \"license\": \"ISC\"\r\n}<\/pre>\n<p>You should now have a <code>package.json<\/code> file that looks similar.<\/p>\n<h3>Futuristic polyfiller<\/h3>\n<p>Chrome was the first browser to use this new style of WebExtension, so the API calls in that browser (and most others) under the <code>chrome.*<\/code> object. However, since things are moving toward a consistently shared API across browsers, these APIs are moving (gradually) to <code>browser.*<\/code>. Most of these APIs also started out as callback-passing functions, but are now moving to a Promise-based API.<\/p>\n<p>Mozilla has created <a href=\"https:\/\/github.com\/mozilla\/webextension-polyfill\">webextension-polyfill<\/a> to normalize these inconsistencies across browsers.<\/p>\n<p>Sadly, though, as of this writing the polyfill is not yet published to npm. So the setup means downloading the code from GitHub, installing dependencies, building it locally, then copying over the polyfill into your project.<\/p>\n<p>That\u2019s a bit more than we can cover here, so I\u2019ll be using the <code>chrome.*<\/code> APIs for this extension.<\/p>\n<p>However, given that the intent of the Browser Extension CG is to move the extension APIs in this direction, it\u2019s not a bad idea to give that a try if you want to get a jump on the rest of us.<\/p>\n<p>For now, we\u2019ll stick <code>chrome.*<\/code> and callbacks.<\/p>\n<h3>Popup powerhouse<\/h3>\n<p>Today we\u2019ll build a web extension that:<\/p>\n<ul>\n<li>shows a list of links from the existing page<\/li>\n<li>the text used in the link<\/li>\n<li>their link relationship (if any)<\/li>\n<\/ul>\n<p>To do that, this WebExtension will use the following things:<\/p>\n<ul>\n<li>a <code>browser_action<\/code> (initially)<\/li>\n<li>a \u201cpinned\u201d popup<\/li>\n<li>an injected \u201ccontent scripts\u201d which we\u2019ll communicate with from the extension<\/li>\n<\/ul>\n<p>To start things off, create a <code>extension\/manifest.json<\/code> in your project directory and add the following JSON:<\/p>\n<pre class=\"brush:php\">{\r\n  \"manifest_version\": 2,\r\n\r\n  \"name\": \"link-extractor\",\r\n  \"version\": \"0.0.1\",\r\n  \"description\": \"Extracts links\",\r\n  \"author\": \"BigBlueHat &lt;byoung@bigbluehat.com&gt;\",\r\n\r\n  \"permissions\": [\r\n    \"activeTab\"\r\n  ],\r\n\r\n  \"browser_action\": {\r\n    \"browser_style\": true,\r\n    \"default_title\": \"View the Links in this Page\",\r\n    \"default_popup\": \"popup.html\",\r\n    \"default_icon\": \"images\/icon32.png\"\r\n  }\r\n}<\/pre>\n<p>This manifest tells the browser the following:<\/p>\n<ul>\n<li>We need the <code>activeTab<\/code> permission so that we can inject scripts into the active tab in the current window.<\/li>\n<li>We have a <code>browser_action<\/code> that adds an icon to the browser UI which opens a pinned popup when clicked.<\/li>\n<\/ul>\n<p>The <code>activeTab<\/code> permission is useful for narrowing the necessary permission grant to just the active tab instead of all the tabs in all the windows. Without this option, we would have to use the <code>&lt;all_urls&gt;<\/code> permission (plus its scary warning message about potentially reading and changing all the pages you visit). Alternatively, we could limit the request to just certain URLs or domains, but we\u2019d still have to ask for the \u201cscary\u201d permission level\u2026but only be able to work in a few places. The <code>activeTab<\/code> permission avoids all of that.<\/p>\n<p>Also, if you don\u2019t absolutely need a certain permission for your extension, don\u2019t ask for it. This is both safer and friendlier to the folks using your extension \u2014 which means more people are likely to use it.<\/p>\n<p>The <code>browser_action<\/code> defines the bits for the pinned popup and the button we will add to the browser\u2019s interface. The <code>popup.html<\/code> file is a simple HTML page. The JavaScript included in that page has access to the special <code>chrome.*<\/code> (or <code>browser.*<\/code> if you\u2019re using the polyfill) extensions APIs which allow us to communicate from the popup to the page in the open tab.<\/p>\n<p>From the popup, we will:<\/p>\n<ul>\n<li>inject a script to extract the links from the <code>activeTab<\/code>\u2018s web page<\/li>\n<li>send that list of links back to the <code>popup.js<\/code> code, which will in turn\u2026<\/li>\n<li>load the list of links into the <code>popup.html<\/code> file<\/li>\n<\/ul>\n<h3>First few files<\/h3>\n<p>Since we\u2019re using the <code>activeTab<\/code> permission and a <code>browser_action<\/code>, we\u2019ll get some initial UI for \u201cfree.\u201d Once installed, there will be an icon (typically) in the top right area of the browser UI. Feel free to use this one while developing:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/icon32.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-16840\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/icon32.png\" alt=\"\" width=\"32\" height=\"32\" \/><\/a><\/p>\n<p>You can save that into your <code>extension\/images\/<\/code> directory as <code>icon32.png<\/code> or make your own.<\/p>\n<p>When that icon is clicked, it will open a pinned popup.<\/p>\n<p>The <code>popup.html<\/code> contents will be loaded into the pinned popup in the UI. The <code>popup.js<\/code> referenced from <code>popup.html<\/code> (see below) will begin its injection, extraction, and communication work on the contents of the active tab.<\/p>\n<p>Here\u2019s what <code>popup.html<\/code> looks like:<\/p>\n<pre class=\"brush:xml\">&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;meta charset=\"utf-8\" \/&gt;\r\n  &lt;style&gt;\r\n    th, td {border-bottom:1px solid #ccc;}\r\n  &lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &lt;table&gt;\r\n    &lt;thead&gt;\r\n      &lt;th&gt;link&lt;\/th&gt;&lt;th&gt;relationship&lt;\/th&gt;\r\n    &lt;\/thead&gt;\r\n    &lt;tbody id=\"links\"&gt;&lt;\/tbody&gt;\r\n  &lt;\/table&gt;\r\n  &lt;script src=\"lib\/jquery-3.2.0.min.js\"&gt;&lt;\/script&gt;\r\n  &lt;script src=\"popup.js\"&gt;&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>Later, we might improve the CSS, but for now, this is plenty. We\u2019ve set up some empty table space to fill in with the link info found in the page, and we\u2019ve included the <code>popup.js<\/code>, which will do the heavy lifting of populating the table.<\/p>\n<p>But first, let\u2019s do a quick test of what\u2019s there already!<\/p>\n<h2>Running WebExtensions<\/h2>\n<p>There are several ways to run our in-progress WebExtension. Each browser comes with its own UI for loading a <code>manifest.json<\/code> out of an \u201cunpacked\u201d extension directory. Here are the URLs to visit or things to click to load the UI in the various (popular) browsers:<\/p>\n<ul>\n<li>Chrome (also Vivaldi &amp; Opera): <code>&lt;chrome:\/\/extensions\/&gt;<\/code>\n<ul>\n<li>click the \u201cDeveloper mode\u201d checkbox (to review the buttons)<\/li>\n<li>click the \u201cLoad unpacked extension\u2026\u201d button<\/li>\n<\/ul>\n<\/li>\n<li>Firefox: <code>&lt;about:debugging&gt;<\/code>\n<ul>\n<li>click the \u201cLoad Temporary Add-on\u201d button<\/li>\n<\/ul>\n<\/li>\n<li>Edge: use the drop-down menu, click \u201cExtensions\u201d\n<ul>\n<li>click the \u201cLoad extension\u201d button<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>So\u2026yeah. Nearly the same, but not quite. Each of these browsers have increasingly more idiosyncratic ways of debugging, however. Since I can\u2019t cover them all here, we\u2019ll stick with using the <code>web-ext<\/code> command-line tool we installed earlier.<\/p>\n<p>From the main project directory, run this on the command line: <code>sh<br \/>\nweb-ext run -s extension\/<\/code><\/p>\n<p>That command will open an \u201cempty\u201d (no previous profile) Firefox with \u201cstock\u201d extensions (Pocket, etc.) and the in-progress extension we\u2019re building here. If everything\u2019s set up correctly, you should see an icon that matches the one here. Click it and you should see the table from <code>popup.html<\/code> above.<\/p>\n<p>I\u2019m hopeful that someday there will be a single command-line tool (perhaps <code>web-ext<\/code>) that will load \u201cempty\u201d versions of other browsers for testing extensions. Until then, finding the right menus and buttons for your preferred browser is your best option.<\/p>\n<p>If you\u2019ve used the command above, you should see that empty Firefox sporting your newly installed WebExtension:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run.png\"><img decoding=\"async\" class=\"aligncenter wp-image-16841\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run.png\" alt=\"\" width=\"860\" height=\"161\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run.png 1130w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run-300x56.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run-768x144.png 768w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/first-run-1024x192.png 1024w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/a><\/p>\n<p>Not much to see yet, but we know that <code>web-ext run<\/code> is working and the files are all loading where they should.<\/p>\n<p>Now, let\u2019s make it work!<\/p>\n<h3>Link extraction<\/h3>\n<p>To keep this extension\u2019s effective code minimal and focused, we\u2019re going to add good ol\u2019 jQuery as a dependency. I know many of you have perhaps \u201cmoved on,\u201d but I\u2019ve found (through trial and error\u2026) that the editorial staff at add-on sites run by Mozilla and Opera greatly prefer libraries they already know. In fact, they\u2019ve automated much of the detection and approval process at this point, so using something they like helps you get through that gate faster.<\/p>\n<p>Head over to <a href=\"http:\/\/jquery.com\/download\">jquery.com\/download<\/a> and grab a copy of jQuery to suit your needs. I downloaded <code>jquery-3.2.0.min.js<\/code>, put it in the <code>extension\/lib<\/code> folder, and added it to the <code>popup.html<\/code> file in the usual web page-y way \u2014 via a <code>&lt;script&gt;<\/code> tag.<\/p>\n<p>So <code>popup.html<\/code> now ends with these two <code>&lt;script&gt;<\/code> tags just above the <code>&lt;\/body&gt;<\/code>:<\/p>\n<pre class=\"brush:xml\">&lt;script src=\"lib\/jquery-3.2.0.min.js\"&gt;&lt;\/script&gt;\r\n  &lt;script src=\"popup.js\"&gt;&lt;\/script&gt;<\/pre>\n<p>Now let\u2019s flesh out the <code>popup.js<\/code> file:<\/p>\n<h3>popup.js<\/h3>\n<p>You\u2019ll notice in the <code>popup.js<\/code> contents below that it uses fairly modern JavaScript. We\u2019re using things like <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Functions\/Arrow_functions#Browser_compatibility\">arrow functions<\/a>, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/let#Browser_compatibility\"><code>let<\/code><\/a> (in place of <code>var<\/code>), and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Template_literals#Browser_compatibility\">template literals\/strings<\/a>. This is possible due to WebExtensions only being supported (in this current format) in fairly recent versions of Firefox, Chrome, etc., which also all support much of ES6\/ECMAScript 2015.<\/p>\n<p>Consequently, we get to write \u201cmodern\u201d JavaScript without the need for any preprocessing. If you do decide to use something even more newfangled (TypeScript, etc.), then you can use the <code>src\/<\/code> folder plus some preprocessing scripts that output the <code>.js<\/code> files into <code>extension\/<\/code>. We\u2019ll avoid that here to keep complexity just a bit lower.<\/p>\n<p><code>extension\/popup.js<\/code> looks like this in all its amazing modernity:<\/p>\n<pre class=\"brush:php\">let $links = $('#links');\r\n\r\nchrome.tabs.executeScript(null, {\r\n  file: 'extractor.js'\r\n}, (results) =&gt; {\r\n  let anchors = results[0];\r\n  anchors.forEach((a) =&gt; {\r\n    let tr = `&lt;tr&gt;\r\n      &lt;td&gt;&lt;a href=\"${a.href}\"&gt;${a.href}&lt;\/a&gt;&lt;br \/&gt;\r\n      ${a.text}\r\n      &lt;\/td&gt;\r\n      &lt;td&gt;${a.rel}&lt;\/td&gt;\r\n    &lt;\/tr&gt;`;\r\n    $links.append(tr);\r\n  });\r\n});<\/pre>\n<p>We start off with a little jQuery for later use to add to the table. Then, we use the <a href=\"https:\/\/developer.chrome.com\/extensions\/tabs#method-executeScript\"><code>chrome.tabs.executeScript()<\/code><\/a> function to inject an <code>extractor.js<\/code> file into the active tab by passing <code>null<\/code> as the value of the first parameter (<code>tabId<\/code>). We then have a callback that receives the <code>results<\/code> of the <code>extractor.js<\/code> code. The value of <code>results<\/code> will be an array from all the places <code>extractor.js<\/code> has run.<\/p>\n<p>Since we\u2019re not running it across multiple frames, it should only have a single value in the array: our list of link info. We then loop through that list of links and populate a table row using a template literal, which we then append (via jQuery) into the <code>tbody<\/code> of our table in <code>popup.html<\/code>.<\/p>\n<p>Phew! Still with me?<\/p>\n<p>Let\u2019s take a look at the <code>extension\/extractor.js<\/code>:<\/p>\n<pre class=\"brush:php\">(function() {\r\n  let anchors = document.querySelectorAll('a[href]');\r\n  let rv = [];\r\n  anchors.forEach((a) =&gt; {\r\n    rv.push({\r\n      href: a.href,\r\n      rel: a.rel,\r\n      text: a.textContent\r\n    });\r\n  });\r\n  return rv;\r\n\/\/ sends the above `rv` as a return value...basically\r\n})();<\/pre>\n<p>The code in <code>extractor.js<\/code> runs upon injection and should end in a value that is a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Web_Workers_API\/Structured_clone_algorithm\">structured clonable<\/a> value. I\u2019m using an anonymous function here to provide:<\/p>\n<ul>\n<li>a private variable namespace<\/li>\n<li>a single, obvious return value<\/li>\n<li>an idempotent function, so the result is the same regardless of being injected\/run multiple times<\/li>\n<\/ul>\n<p>You\u2019ll note I\u2019m using <code>document.querySelectorAll<\/code> here rather than jQuery. You could of course inject jQuery also, but that takes longer and costs more in processing time than the value it would get us for this tiny bit of code.<\/p>\n<p>Awesome. Let\u2019s make sure your files match mine, and then run this thing one last time.<\/p>\n<p>Here\u2019s my directory layout:<\/p>\n<pre class=\"brush:php\">.\r\n+-- extension\r\n\u00b6   +-- extractor.js\r\n\u00b6   +-- images\r\n\u00b6   \u00b6   +-- icon32.png\r\n\u00b6   +-- lib\r\n\u00b6   \u00b6   +-- jquery-3.2.0.min.js\r\n\u00b6   +-- manifest.json\r\n\u00b6   +-- popup.html\r\n\u00b6   +-- popup.js\r\n+-- package.json\r\n\r\n4 directories, 10 files<\/pre>\n<p>If yours matches, then let\u2019s run this thing!<\/p>\n<p><code>web-ext run -s extension\/<\/code><\/p>\n<p>Alternatively, you can load it via your preferred browser\u2019s add-on debugging UI (see the \u201cRunning WebExtensions\u201d section above).<\/p>\n<p>Here\u2019s what it should look like now:<\/p>\n<p><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted.png\"><img decoding=\"async\" class=\"aligncenter wp-image-16842\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted.png\" alt=\"\" width=\"860\" height=\"513\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted.png 1708w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted-300x179.png 300w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted-768x458.png 768w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2017\/04\/links-extracted-1024x610.png 1024w\" sizes=\"(max-width: 860px) 100vw, 860px\" \/><\/a><\/p>\n<h2>Wrapping Up<\/h2>\n<p>If all went well, you have a working WebExtension that you can run in your own browser. However, distributing it is a completely different story.<\/p>\n<p>The steps are essentially:<\/p>\n<ul>\n<li>package the WebExtensions into a single (zip-based) file archive<\/li>\n<li>upload it to one or more add-on stores<\/li>\n<\/ul>\n<p>It\u2019s at point no. 2 where things get weird\u2026or at least varied.<\/p>\n<p>Some examples of publishing your WebExtension:<\/p>\n<ul>\n<li>Firefox and Opera can take up to a month for their editorial teams to review your code<\/li>\n<li>publication is completely free (so there\u2019s that)<\/li>\n<li>Chrome\u2019s add-on store costs you $5USD for the first \u201c20 items\u201d<\/li>\n<li>instant publishing (well\u2026within an hour), but no editorial review<\/li>\n<li>Microsoft Edge is a unique beast and requires a full-on Microsoft Dev Center registration<\/li>\n<li>currently that\u2019s $19USD for individuals or $99USD for companies<\/li>\n<li>not sure what (if any) editorial process happens does open the door to publishing any kind of Windows-based app (which is likely the point of the requirement)<\/li>\n<\/ul>\n<p>For now, let\u2019s just bundle up this little extension and prep it for the free <a href=\"http:\/\/addons.mozilla.org\/\">Mozilla Add-ons site<\/a>. We\u2019ll use <code>web-ext<\/code> again:<\/p>\n<p><code>$ web-ext build -s extension\/<\/code><\/p>\n<p>You should now see the <code>web-ext-artifacts\/<\/code> folder with a <code>link-extractor-0.0.1.zip<\/code> in your project directory.<\/p>\n<p>Next, let\u2019s visit the <a href=\"https:\/\/addons.mozilla.org\/en-US\/developers\/addon\/submit\/distribution\">Submit a New Add-on<\/a> page.<\/p>\n<p>On that page, choose the \u201cOn this site\u201d option. Self-hosted distribution is possible, but it\u2019s a bit more complex so we\u2019ll (sadly) have to skip it today.<\/p>\n<p>On the next page, click \u201cSelect a file\u2026\u201d and choose the <code>.zip<\/code> file created earlier. An automated review will run which will generate a report. Likely for something this simple, it will only contain warnings (if anything) and often those will be in other people\u2019s code (like jQuery). The editorial staff will also see these warnings (or errors) and may reference them in their review.<\/p>\n<p>On the next page, you\u2019ll have the opportunity to add listing information to help people find your extension. At the bottom of this page is the \u201cSubmit Version for Review\u201d button. Click that\u2026and hope for the best!<\/p>\n<p>In my case, I hit a small snag. There was already an extension using the <code>link-extractor<\/code> \u201cslug\u201d in the Mozilla Add-on store, so I changed the slug <code>link-info-extractor<\/code> and will likely now rename the project to that.<\/p>\n<p>Often, you\u2019ll find it\u2019s these little annoyances (more than anything) that will slow you down when building WebExtensions. They\u2019re not technically \u201chard,\u201d but because of all the not-quite-compatible APIs, stores, and distribution models, there can be a disproportionate amount of time spent on resizing icons and screenshots versus coding the extension itself.<\/p>\n<p>There\u2019s some likelihood that over time these wrinkles will get ironed out either by the browsers or by your friendly neighborhood polyfill developer. We\u2019ll see.<\/p>\n<h2>Conclusion<\/h2>\n<p>If you know HTML, CSS, and JS, <a href=\"https:\/\/www.webcodegeeks.com\/web-development\/building-web-extensions-can\/\">you have what you need to build WebExtensions<\/a> that can do all kinds of things. In addition to this tech know-how though, you\u2019ll need a daily dose of patience as you jump between documentation and distribution sites for the various browsers and their (still weirdly proprietary) stores.<\/p>\n<p>In the end, you may conclude (as I have) that \u201cWebExtensions\u201d is a bit of a misnomer and the \u201cBrowserExtension\u201d term used by the <a href=\"https:\/\/www.w3.org\/community\/browserext\/\">Community Group<\/a> is more accurate. The unique APIs, the still-in-progress collaboration, and the very-not-webby distribution models will likely trip you up as you go.<\/p>\n<p>However, if there\u2019s an \u201calways on\u201d sort of customization you want, or a piece of futuristic proof-of-concept you\u2019d like to experiment with now, WebExtensions (or BrowserExtensions) are the best available tool with the least amount of overhead. At least today.<\/p>\n<h2>Addendum: Getting Through The Gate<\/h2>\n<p>I sent this little WebExtension through the Mozilla Add-on editorial process, and here\u2019s what I ran into using the code above:<\/p>\n<ul>\n<li>they didn\u2019t like jQuery\u2026this time<\/li>\n<li>the template literal\/string + <code>$.append()<\/code> === an <code>innerHTML()<\/code> call (bad)<\/li>\n<li>and I\u2019d left some junk laying about in <code>extension\/<\/code><\/li>\n<\/ul>\n<p>Consequently, I did some housecleaning and code rewriting and resubmitted it. It passed with flying colors that time \u2014 <a href=\"https:\/\/addons.mozilla.org\/en-US\/firefox\/addon\/link-info-extractor\/\">check it out!<\/a>.<\/p>\n<p>Here\u2019s what I changed:<\/p>\n<p>First, I removed the <code>extension\/lib\/mustache.min.js<\/code> file I\u2019d been pondering using, but forgot to remove when I decided not to. This was their primary complaint actually and the reason for the rejection of v0.0.1.<\/p>\n<p>Second, they asked me to improve the code to not use jQuery (it being a rather large dependency for such little code) and to avoid using <code>innerHTML()<\/code>-based DOM manipulation. To fix those, I rewrote the template-based bits to pure-DOM calls (ugly\u2026but effective) and by doing so avoided the <code>innerHTML()<\/code> usage.<\/p>\n<p>Here\u2019s the new <code>popup.js<\/code> file sans-jQuery:<\/p>\n<pre class=\"brush:js\">links = document.getElementById('links');\r\n\r\nchrome.tabs.executeScript(null, {\r\n  file: 'extractor.js'\r\n}, (results) =&gt; {\r\n  let anchors = results[0];\r\n  anchors.forEach((a) =&gt; {\r\n    let tr = document.createElement('tr');\r\n      let td_url = document.createElement('td');\r\n        let link = document.createElement('a');\r\n        link.href = a.href;\r\n        link.target = '_blank';\r\n        link.textContent = a.href;\r\n        let br = document.createElement('br');\r\n      td_url.append(link, br, a.text);\r\n\r\n      let td_rel = document.createElement('td');\r\n      td_rel.textContent = a.rel;\r\n    tr.append(td_url, td_rel);\r\n    links.append(tr);\r\n  });\r\n});<\/pre>\n<p>It\u2019s a bit more code (because native-DOM is a bit wordier than jQuery). It was (in the end) worth it to remove the dependency, the subsequent load time, and to avoid the use of <code>innerHTML()<\/code>.<\/p>\n<p>The review process must have been expedited (it being an update and all), because it was cleared later the same day! <a href=\"https:\/\/addons.mozilla.org\/en-US\/firefox\/addon\/link-info-extractor\/\">Check it out in the Mozilla Add-ons site.<\/a><\/p>\n<p>I\u2019ve also <a href=\"https:\/\/github.com\/bigbluehat\/link-info-extractor\">put the code up on GitHub<\/a> and linked to that as the \u201cSupport Website\u201d on the Add-ons site.<\/p>\n<p>There\u2019s obviously more to be done in both places to make this a well-promoted extension, but that\u2019s all polish (screenshots, a meaningful description) to be added soon \u2014 or as needed or wanted (your needs may vary).<\/p>\n<p>Lastly, I find it prudent to publish to the Mozilla Add-on site first. The additional code review (as seen above) is helpful, and it\u2019s likely they\u2019ll see something you\u2019ve missed with regards to performance, security, or code quality. Having this editorial process started earlier can save you lots of time later as you work to ship quality WebExtensions to all the other browser add-on stores.<\/p>\n<p>Your mileage will certainly vary, but the feedback is valuable. The increased understanding of what browsers want will also serve to make you a better Web developer. Enjoy that. I do.<\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"https:\/\/blog.codeship.com\/webextensions-101\/\">WebExtensions 101<\/a> from our <a href=\"http:\/\/www.webcodegeeks.com\/join-us\/wcg\/\">WCG partner<\/a>\u00a0Benjamin Young at the <a href=\"http:\/\/blog.codeship.com\/\">Codeship Blog<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one might have needed to know C or Objective-C and the internal workings of the browser\u2019s own code or even the operating system\u2019s idiosyncrasies. Thankfully, there\u2019s been some &hellip;<\/p>\n","protected":false},"author":158,"featured_media":927,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-16837","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>WebExtensions 101 - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"WebExtensions 101 - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2017-04-06T09:15:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Benjamin Cane\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Benjamin Cane\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"21 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\"},\"author\":{\"name\":\"Benjamin Cane\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4f5d918df9c19fab91b5b205357ce0b8\"},\"headline\":\"WebExtensions 101\",\"datePublished\":\"2017-04-06T09:15:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\"},\"wordCount\":3543,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"articleSection\":[\"Web Dev\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\",\"name\":\"WebExtensions 101 - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"datePublished\":\"2017-04-06T09:15:00+00:00\",\"description\":\"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Web Dev\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/web-development\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"WebExtensions 101\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4f5d918df9c19fab91b5b205357ce0b8\",\"name\":\"Benjamin Cane\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/09c6af2f1a7430456089189937094b817ef1b7c75ab9968bfd3ec35d938d914b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/09c6af2f1a7430456089189937094b817ef1b7c75ab9968bfd3ec35d938d914b?s=96&d=mm&r=g\",\"caption\":\"Benjamin Cane\"},\"description\":\"Benjamin Cane is a systems architect in the financial services industry. He writes about Linux systems administration on his blog and has recently published his first book, Red Hat Enterprise Linux Troubleshooting Guide.\",\"url\":\"https:\/\/www.webcodegeeks.com\/author\/benjamin-cane\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"WebExtensions 101 - Web Code Geeks - 2026","description":"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/","og_locale":"en_US","og_type":"article","og_title":"WebExtensions 101 - Web Code Geeks - 2026","og_description":"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one","og_url":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2017-04-06T09:15:00+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","type":"image\/jpeg"}],"author":"Benjamin Cane","twitter_card":"summary_large_image","twitter_creator":"@webcodegeeks","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Benjamin Cane","Est. reading time":"21 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/"},"author":{"name":"Benjamin Cane","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4f5d918df9c19fab91b5b205357ce0b8"},"headline":"WebExtensions 101","datePublished":"2017-04-06T09:15:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/"},"wordCount":3543,"commentCount":0,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","articleSection":["Web Dev"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/","url":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/","name":"WebExtensions 101 - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","datePublished":"2017-04-06T09:15:00+00:00","description":"If you know the basic web technology stack (HTML, JS, and CSS), then you can indeed build a WebExtension. It wasn\u2019t always this simple. In the past, one","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/web-dev-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/web-development\/webextensions-101\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Web Dev","item":"https:\/\/www.webcodegeeks.com\/category\/web-development\/"},{"@type":"ListItem","position":3,"name":"WebExtensions 101"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/4f5d918df9c19fab91b5b205357ce0b8","name":"Benjamin Cane","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/09c6af2f1a7430456089189937094b817ef1b7c75ab9968bfd3ec35d938d914b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/09c6af2f1a7430456089189937094b817ef1b7c75ab9968bfd3ec35d938d914b?s=96&d=mm&r=g","caption":"Benjamin Cane"},"description":"Benjamin Cane is a systems architect in the financial services industry. He writes about Linux systems administration on his blog and has recently published his first book, Red Hat Enterprise Linux Troubleshooting Guide.","url":"https:\/\/www.webcodegeeks.com\/author\/benjamin-cane\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/16837","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/158"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=16837"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/16837\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/927"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=16837"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=16837"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=16837"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}