Creating One Browser Extension For All Browsers: Edge, Chrome, Firefox, Opera, Brave And Vivaldi

Sponsored PostCreating One Browser Extension For All Browsers: Edge, Chrome, Firefox, Opera, Brave And Vivaldi

In today’s article, we’ll create a JavaScript extension that works in all major modern browsers, using the very same code base. Indeed, the Chrome extension model based on HTML, CSS and JavaScript is now available almost everywhere, and there is even a Browser Extension Community Group1 working on a standard.

I’ll explain how you can install this extension that supports the web extension model (i.e. Edge, Chrome, Firefox, Opera, Brave and Vivaldi), and provide some simple tips on how to get a unique code base for all of them, but also how to debug in each browser.

Note: We won’t cover Safari in this article because it doesn’t support the same extension model2 as others.

Further Reading on SmashingMag: Link

Basics Link

I won’t cover the basics of extension development because plenty of good resources are already available from each vendor:

So, if you’ve never built an extension before or don’t know how it works, have a quick look at those resources. Don’t worry: Building one is simple and straightforward.

Our Extension Link

Let’s build a proof of concept — an extension that uses artificial intelligence (AI) and computer vision to help the blind analyze images on a web page.

We’ll see that, with a few lines of code, we can create some powerful features in the browser. In my case, I’m concerned with accessibility on the web and I’ve already spent some time thinking about how to make a breakout game accessible using web audio and SVG14, for instance.

Still, I’ve been looking for something that would help blind people in a more general way. I was recently inspired while listening to a great talk by Chris Heilmann15 in Lisbon: “Pixels and Hidden Meaning in Pixels16.”

Indeed, using today’s AI algorithms in the cloud, as well as text-to-speech technologies, exposed in the browser with the Web Speech API17 or using a remote cloud service, we can very easily build an extension that analyzes web page images with missing or improperly filled alt text properties.

My little proof of concept simply extracts images from a web page (the one in the active tab) and displays the thumbnails in a list. When you click on one of the images, the extension queries the Computer Vision API to get some descriptive text for the image and then uses either the Web Speech API or Bing Speech API to share it with the visitor.

The video below demonstrates it in Edge, Chrome, Firefox, Opera and Brave.

You’ll notice that, even when the Computer Vision API is analyzing some CGI images, it’s very accurate! I’m really impressed by the progress the industry has made on this in recent months.

I’m using these services:

  • Computer Vision API18, Microsoft Cognitive Services

    This is free to use19 (with a quota). You’ll need to generate a free key; replace the TODO section in the code with your key to make this extension work on your machine. To get an idea of what this API can do, play around with it20.
  • 21
  • Bing Text to Speech API22, Microsoft Cognitive Services

    This is also free to use23 (with a quota, too). You’ll need to generate a free key again. We’ll also use a small library24 that I wrote recently to call this API from JavaScript. If you don’t have a Bing key, the extension will always fall back to the Web Speech API, which is supported by all recent browsers.

But feel free to try other similar services:

You can find the code for this small browser extension on my GitHub page27. Feel free to modify the code for other products you want to test.

Tip To Make Your Code Compatible With All Browsers Link

Most of the code and tutorials you’ll find use the namespace chrome.xxx for the Extension API (chrome.tabs, for instance).

But, as I’ve said, the Extension API model is currently being standardized to browser.xxx, and some browsers are defining their own namespaces in the meantime (for example, Edge is using msBrowser).

Fortunately, most of the API remains the same behind the browser. So, it’s very simple to create a little trick to support all browsers and namespace definitions, thanks to the beauty of JavaScript:

window.browser = (function () { return window.msBrowser || window.browser || window.chrome; })(); 

And voilà!

Of course, you’ll also need to use the subset of the API supported by all browsers. For instance:

Extension Architecture Link

Let’s review together the architecture of this extension. If you’re new to browser extensions, this should help you to understand the flow.

Let’s start with the manifest file31:

Slide132
(View large version33)

This manifest file and its associated JSON is the minimum you’ll need to load an extension in all browsers, if we’re not considering the code of the extension itself, of course. Please check the source34 in my GitHub account, and start from here to be sure that your extension is compatible with all browsers.

For instance, you must specify an author property to load it in Edge; otherwise, it will throw an error. You’ll also need to use the same structure for the icons. The default_title property is also important because it’s used by screen readers in some browsers.

Here are links to the documentation to help you build a manifest file that is compatible everywhere:

The sample extension used in this article is mainly based on the concept of the content script38. This is a script living in the context of the page that we’d like to inspect. Because it has access to the DOM, it will help us to retrieve the images contained in the web page. If you’d like to know more about what a content script is, Opera39, Mozilla40 and Google41 have documentation on it.

Our content script42 is simple:

Slide243
(View large version44)
console.log("Dare Angel content script started"); browser.runtime.onMessage.addListener(function (request, sender, sendResponse) { if (request.command == "requestImages") { var images = document.getElementsByTagName('img'); var imagesList = []; for (var i = 0; i  64 && images[i].height > 64)) { imagesList.push({ url: images[i].src, alt: images[i].alt }); } } sendResponse(JSON.stringify(imagesList)); } }); view raw 

This first logs into the console to let you check that the extension has properly loaded. Check it via your browser’s developer tool, accessible from F12, Control + Shift + I or ⌘ + ⌥ + I.

It then waits for a message from the UI page with a requestImages command to get all of the images available in the current DOM, and then it returns a list of their URLs if they’re bigger than 64 × 64 pixels (to avoid all of the pixel-tracking junk and low-resolution images).

Slide345
(View large version46)

The popup UI page47 we’re using is very simple and will display the list of images returned by the content script inside a flexbox container48. It loads the start.js script, which immediately creates an instance of dareangel.dashboard.js49 to send a message to the content script to get the URLs of the images in the currently visible tab.

Here’s the code that lives in the UI page, requesting the URLs to the content script:

browser.tabs.query({ active: true, currentWindow: true }, (tabs) => { browser.tabs.sendMessage(tabs[0].id, { command: "requestImages" }, (response) => { this._imagesList = JSON.parse(response); this._imagesList.forEach((element) => { var newImageHTMLElement = document.createElement("img"); newImageHTMLElement.src = element.url; newImageHTMLElement.alt = element.alt; newImageHTMLElement.tabIndex = this._tabIndex; this._tabIndex++; newImageHTMLElement.addEventListener("focus", (event) => { if (COMPUTERVISIONKEY !== "") { this.analyzeThisImage(event.target.src); } else { var warningMsg = document.createElement("div"); warningMsg.innerHTML = "

Please generate a Computer Vision key in the other tab. Link

"; this._targetDiv.insertBefore(warningMsg, this._targetDiv.firstChild); browser.tabs.create({ active: false, url: "https://www.microsoft.com/cognitive-services/en-US/sign-up?ReturnUrl=/cognitive-services/en-us/subscriptions?productId=%2fproducts%2f54d873dd5eefd00dc474a0f4" }); } }); this._targetDiv.appendChild(newImageHTMLElement); }); }); });

We’re creating image elements. Each image will trigger an event if it has focus, querying the Computer Vision API for review.

This is done by this simple XHR call:

analyzeThisImage(url) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { if (xhr.readyState == 4 && xhr.status == 200) { var response = document.querySelector('#response'); var reponse = JSON.parse(xhr.response); var resultToSpeak = `With a confidence of ${Math.round(reponse.description.captions[0].confidence * 100)}%, I think it's ${reponse.description.captions[0].text}`; console.log(resultToSpeak); if (!this._useBingTTS || BINGSPEECHKEY === "") { var synUtterance = new SpeechSynthesisUtterance(); synUtterance.text = resultToSpeak; window.speechSynthesis.speak(synUtterance); } else { this._bingTTSclient.synthesize(resultToSpeak); } } }; xhr.onerror = (evt) => { console.log(evt); }; try { xhr.open('POST', 'https://api.projectoxford.ai/vision/v1.0/describe'); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Ocp-Apim-Subscription-Key", COMPUTERVISIONKEY); var requestObject = { "url": url }; xhr.send(JSON.stringify(requestObject)); } catch (ex) { console.log(ex); } } view raw 

The following articles will you help you to understand how this Computer Vision API works:

  • Analyzing an Image Version 1.050,” Microsoft Cognitive Services
  • Computer Vision API, v1.051,” Microsoft Cognitive Services

    This shows you via an interactive console in a web page how to call the REST API with the proper JSON properties, and the JSON object you’ll get in return. It’s useful to understand how it works and how you will call it.

In our case, we’re using the describe feature of the API. You’ll also notice in the callback that we will try to use either the Web Speech API or the Bing Text-to-Speech service, based on your options.

Here, then, is the global workflow of this little extension:

Slide452
(View large version53)

Loading The Extension In Each Browser Link

Let’s review quickly how to install the extension in each browser.

Prerequisites Link

Download or clone my small extension54 from GitHub somewhere to your hard drive.

Also, modify dareangel.dashboard.js to add at least a Computer Vision API key. Otherwise, the extension will only be able to display the images extracted from the web page.

Microsoft Edge Link

First, you’ll need at least a Windows 10 Anniversary Update (OS Build 14393+) to have support for extensions in Edge.

Then, open Edge and type about:flags in the address bar. Check the “Enable extension developer features.”

EnableInEdge00155

Click on “…” in the Edge’s navigation bar and then “Extensions” and then “Load extension,” and select the folder where you’ve cloned my GitHub repository. You’ll get this:

56

Click on this freshly loaded extension, and enable “Show button next to the address bar.”

EnableInEdge00357

Note the “Reload extension” button, which is useful while you’re developing your extension. You won’t be forced to remove or reinstall it during the development process; just click the button to refresh the extension.

Navigate to BabylonJS626158, and click on the Dare Angel (DA) button to follow the same demo as shown in the video.

Google Chrome, Opera, Vivaldi Link

In Chrome, navigate to chrome://extensions. In Opera, navigate to opera://extensions. And in Vivaldi, navigate to vivaldi://extensions. Then, enable “Developer mode.”

Click on “Load unpacked extension,” and choose the folder where you’ve extracted my extension.

Chrome00159
(View large version60)

Navigate to BabylonJS626158, and open the extension to check that it works fine.

Mozilla Firefox Link

You’ve got two options here. The first is to temporarily load your extension, which is as easy as it is in Edge and Chrome.

Open Firefox, navigate to about:debugging and click “Load Temporary Add-on.” Then, navigate to the folder of the extension, and select the manifest.json file. That’s it! Now go to BabylonJS626158 to test the extension.

Firefox00163
(View large version64)

The only problem with this solution is that every time you close the browser, you’ll have to reload the extension. The second option would be to use the XPI packaging. You can learn more about this in “Extension Packaging65” on the Mozilla Developer Network.

Brave Link

The public version of Brave doesn’t have a “developer mode” embedded in it to let you load an unsigned extension. You’ll need to build your own version of it by following the steps in “Loading Chrome Extensions in Brave66.”

As explained in that article, once you’ve cloned Brave, you’ll need to open the extensions.js file in a text editor. Locate the lines below, and insert the registration code for your extension. In my case, I’ve just added the two last lines:

 // Manually install the braveExtension and torrentExtension extensionInfo.setState(config.braveExtensionId, extensionStates.REGISTERED) loadExtension(config.braveExtensionId, getExtensionsPath('brave'), generateBraveManifest(), 'component') extensionInfo.setState('DareAngel', extensionStates.REGISTERED) loadExtension('DareAngel', getExtensionsPath('DareAngel/')) view raw 

Copy the extension to the app/extensions folder. Open two command prompts in the browser-laptop folder. In the first one, launch npm run watch, and wait for webpack to finish building Brave’s Electron app. It should say, “webpack: bundle is now VALID.” Otherwise, you’ll run into some issues.

Brave00167
(View large version68)

Then, in the second command prompt, launch npm start, which will launch our slightly custom version of Brave.

In Brave, navigate to about:extensions, and you should see the extension displayed and loaded in the address bar.

Brave00269
(View large version70)

Debugging The Extension In Each Browser Link

Tip for all browsers: Using console.log(), simply log some data from the flow of your extension. Most of the time, using the browser’s developer tools, you’ll be able to click on the JavaScript file that has logged it to open it and debug it.

Microsoft Edge Link

To debug the client script part, living in the context of the page, you just need to open F12. Then, click on the “Debugger” tab and find your extension’s folder.

Open the script file that you’d like to debug — dareangel.client.js, in my case — and debug your code as usual, setting up breakpoints, etc.

DebugInEdge00171
(View large version72)

If your extension creates a separate tab to do its job (like the Page Analyzer73, which our Vorlon.js74 team published in the store), simply press F12 on that tab to debug it.

DebugInEdge00275
(View large version76)

If you’d like to debug the popup page, you’ll first need to get the ID of your extension. To do that, simply go into the property of the extension and you’ll find an ID property:

DebugInEdge00377

Then, you’ll need to type in the address bar something like ms-browser-extension://ID_of_your_extension/yourpage.html. In our case, it would be ms-browser-extension://DareAngel_vdbyzyarbfgh8/dashboard.html. Then, simply use F12 on this page:

DebugInEdge00478
(View large version79)

Google Chrome, Opera, Vivaldi, Brave Link

Because Chrome and Opera rely on the same Blink code base, they share the same debugging process. Even though Brave and Vivaldi are forks of Chromium, they also share the same debugging process most of the time.

To debug the client script part, open the browser’s developer tools on the page that you’d like to debug (pressing F12, Control + Shift + I or ⌘ + ⌥ + I, depending on the browser or platform you’re using).

Then, click on the “Content scripts” tab and find your extension’s folder. Open the script file that you’d like to debug, and debug your code just as you would do with any JavaScript code.

DebugInChrome00180
(View large version81)

To debug a tab that your extension would create, it’s exactly the same as with Edge: Simply use the developer tools.

DebugInChrome00282
(View large version83)

For Chrome and Opera, to debug the popup page, right-click on the button of your extension next to the address bar and choose “Inspect popup,” or open the HTML pane of the popup and right-click inside it to “Inspect.” Vivaldi only supports right-click and then “Inspect” inside the HTML pane once opened.

DebugInChrome00384
(View large version85)

For Brave, it’s the same process as with Edge. You first need to find the GUID associated with your extension in about:extensions:

BraveDebug00186

And then, in a separate tab, open the page you’d like to debug like — in my case, chrome-extension://bodaahkboijjjodkbmmddgjldpifcjap/dashboard.html — and open developer tools.

BraveDebug00287
(View large version88)

For the layout, you have a bit of help using Shift + F8, which will let you inspect the complete frame of Brave. And you’ll discover that Brave is an Electron app using React!

Note, for instance, the data-reactroot attribute.

BraveDebug00389
(View large version90)

Note: I had to slightly modify the CSS of the extension for Brave because it currently displays popups with a transparent background by default, and I also had some issues with the height of my images collection. I’ve limited it to four elements in Brave.

Mozilla Firefox Link

Mozilla has really great documentation on debugging web extensions91.

For the client script part, it’s the same as in Edge, Chrome, Opera and Brave. Simply open the developer tools in the tab you’d like to debug, and you’ll find a moz-extension://guid section with your code to debug:

DebugInFirefox00192
(View large version93)

If you need to debug a tab that your extension would create (like Vorlon.js’ Page Analyzer extension), simply use the developer tools:

DebugInFirefox00294
(View large version95)

Finally, debugging a popup is a bit more complex but is well explained in the “Debugging Popups96” section of the documentation.

DebugInFirefox00397
(View large version98)

Publishing Your Extension In Each Store Link

Each vendor has detailed documentation on the process to follow to publish your extension in its store. They all take similar approaches. You need to package the extension in a particular file format — most of the time, a ZIP-like container. Then, you have to submit it in a dedicated portal, choose a pricing model and wait for the review process to complete. If accepted, your extension will be downloadable in the browser itself by any user who visits the extensions store.

Here are the various processes:

Please note that submitting a Microsoft Edge extension to the Windows Store is currently a restricted capability. Reach out to the Microsoft Edge team103 with your request to be a part of the Windows Store, and they’ll consider you for a future update.

I’ve tried to share as much of what I’ve learned from working on our Vorlon.js Page Analyzer extension104 and this little proof of concept.

Some developers remember the pain of working through various implementations to build their extension — whether it meant using different build directories, or working with slightly different extension APIs, or following totally different approaches, such as Firefox’s XUL extensions or Internet Explorer’s BHOs and ActiveX.

It’s awesome to see that, today, using our regular JavaScript, CSS and HTML skills, we can build great extensions using the very same code base and across all browsers!

Feel free to ping me on Twitter105 for any feedback.

(ms, vf, rb, yk, al, il)

Footnotes Link

  1. 1 https://browserext.github.io/
  2. 2 https://developer.apple.com/reference/safariextensions
  3. 3 https://www.smashingmagazine.com/2014/11/creating-save-later-chrome-extension-modern-web-tools/
  4. 4 https://www.smashingmagazine.com/2016/10/whats-the-deal-with-the-samsung-internet-browser/
  5. 5 https://www.smashingmagazine.com/2015/05/form-inputs-browser-support-issue/
  6. 6 https://www.smashingmagazine.com/2015/09/chrome-firefox-safari-opera-edge-impressive-web-browser-alternatives/
  7. 7 https://developer.chrome.com/extensions
  8. 8 https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/extensions/
  9. 9 https://channel9.msdn.com/Events/WebPlatformSummit/edgesummit2016/ES1614?wt.mc_id=DX_879946&OC.ID=DX_879946&CR_CC=DX_879946
  10. 10 https://developer.mozilla.org/en-US/Add-ons/WebExtensions
  11. 11 https://wiki.mozilla.org/WebExtensions
  12. 12 https://dev.opera.com/extensions/getting-started/
  13. 13 https://github.com/brave/browser-laptop/wiki/Developer-Notes-on-Installing-or-Updating-Extensions
  14. 14 https://www.davrous.com/2015/08/27/creating-an-accessible-breakout-game-using-web-audio-svg/
  15. 15 https://twitter.com/codepo8
  16. 16 https://www.youtube.com/watch?v=TWVNTsdm27U
  17. 17 https://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html#tts-section
  18. 18 https://www.microsoft.com/cognitive-services/en-us/computer-vision-api
  19. 19 https://www.microsoft.com/cognitive-services/en-us/subscriptions?productId=/products/54d873dd5eefd00dc474a0f4
  20. 20 https://www.captionbot.ai/
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/2016/12/CaptionBot-1-preview-opt.png
  22. 22 https://www.microsoft.com/cognitive-services/en-us/speech-api
  23. 23 https://www.microsoft.com/cognitive-services/en-us/subscriptions?productId=/products/Bing.Speech.Preview
  24. 24 https://github.com/davrous/BingTTSClientJSLib
  25. 25 https://visual-recognition-demo.mybluemix.net/
  26. 26 https://cloud.google.com/vision/
  27. 27 http://github.com/davrous/dareangel
  28. 28 https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/extensions/api-support/
  29. 29 https://developer.mozilla.org/en-US/Add-ons/WebExtensions
  30. 30 https://dev.opera.com/extensions/apis/
  31. 31 https://github.com/davrous/dareangel/blob/master/manifest.json
  32. 32 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide1-large-opt.png
  33. 33 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide1-large-opt.png
  34. 34 https://github.com/davrous/dareangel/blob/master/manifest.json
  35. 35 https://developer.chrome.com/extensions/manifest
  36. 36 https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/extensions/api-support/supported-manifest-keys/
  37. 37 https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json
  38. 38 https://dev.opera.com/extensions/architecture-overview/#the-content-script
  39. 39 https://dev.opera.com/extensions/content-scripts/
  40. 40 https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts
  41. 41 https://developer.chrome.com/extensions/content_scripts
  42. 42 https://github.com/davrous/dareangel/blob/master/dareangel.client.ts
  43. 43 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide2-large-opt.png
  44. 44 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide2-large-opt.png
  45. 45 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide3-large-opt.png
  46. 46 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Slide3-large-opt.png
  47. 47 https://github.com/davrous/dareangel/blob/master/dashboard.html
  48. 48 https://github.com/davrous/dareangel/blob/master/dashboard.css
  49. 49 https://github.com/davrous/dareangel/blob/master/dareangel.dashboard.js
  50. 50 https://www.microsoft.com/cognitive-services/en-us/Computer-Vision-API/documentation/AnalyzeImage
  51. 51 https://dev.projectoxford.ai/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fa
  52. 52 https://www.smashingmagazine.com/wp-content/uploads/2016/12/xSlide4_thumb-large-opt.png
  53. 53 https://www.smashingmagazine.com/wp-content/uploads/2016/12/xSlide4_thumb-large-opt.png
  54. 54 https://github.com/davrous/dareangel
  55. 55 https://www.smashingmagazine.com/wp-content/uploads/2016/12/EnableInEdge001-1-preview-opt.png
  56. 56 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Edge001-preview-opt.png
  57. 57 https://www.smashingmagazine.com/wp-content/uploads/2016/12/EnableInEdge003-1-preview-opt.png
  58. 58 http://www.babylonjs.com/
  59. 59 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Chrome001-large-opt.png
  60. 60 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Chrome001-large-opt.png
  61. 61 http://www.babylonjs.com/
  62. 62 http://www.babylonjs.com/
  63. 63 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Firefox001-large-opt.png
  64. 64 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Firefox001-large-opt.png
  65. 65 https://developer.mozilla.org/en-US/Add-ons/Extension_Packaging
  66. 66 https://blog.brave.com/loading-chrome-extensions-in-brave/
  67. 67 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Brave001-large-opt.png
  68. 68 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Brave001-large-opt.png
  69. 69 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Brave002-large-opt.png
  70. 70 https://www.smashingmagazine.com/wp-content/uploads/2016/12/Brave002-large-opt.png
  71. 71 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge001-large-opt.png
  72. 72 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge001-large-opt.png
  73. 73 https://www.microsoft.com/store/p/page-analyzer/9nblggh4qws7
  74. 74 http://www.vorlonjs.io/
  75. 75 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge002-large-opt.png
  76. 76 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge002-large-opt.png
  77. 77 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge003-preview-opt.png
  78. 78 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge004-large-opt.png
  79. 79 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInEdge004-large-opt.png
  80. 80 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome001-large-opt.png
  81. 81 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome001-large-opt.png
  82. 82 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome002-large-opt.png
  83. 83 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome002-large-opt.png
  84. 84 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome003-1-large-opt.jpg
  85. 85 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInChrome003-1-large-opt.jpg
  86. 86 https://www.smashingmagazine.com/wp-content/uploads/2016/12/BraveDebug001-preview-opt.png
  87. 87 https://www.smashingmagazine.com/wp-content/uploads/2016/12/BraveDebug002-large-opt.png
  88. 88 https://www.smashingmagazine.com/wp-content/uploads/2016/12/BraveDebug002-large-opt.png
  89. 89 https://www.smashingmagazine.com/wp-content/uploads/2016/12/BraveDebug003-large-opt.jpg
  90. 90 https://www.smashingmagazine.com/wp-content/uploads/2016/12/BraveDebug003-large-opt.jpg
  91. 91 https://developer.mozilla.org/fr/Add-ons/WebExtensions/Debugging
  92. 92 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox001-large-opt.png
  93. 93 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox001-large-opt.png
  94. 94 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox002-large-opt.png
  95. 95 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox002-large-opt.png
  96. 96 https://developer.mozilla.org/fr/Add-ons/WebExtensions/Debugging
  97. 97 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox003-large-opt.jpg
  98. 98 https://www.smashingmagazine.com/wp-content/uploads/2016/12/DebugInFirefox003-large-opt.jpg
  99. 99 https://developer.chrome.com/webstore/publish
  100. 100 https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Publishing_your_WebExtension
  101. 101 https://dev.opera.com/extensions/publishing-guidelines/
  102. 102 https://docs.microsoft.com/en-us/microsoft-edge/extensions/guides/packaging
  103. 103 http://aka.ms/extension-request
  104. 104 https://www.microsoft.com/store/p/page-analyzer/9nblggh4qws7
  105. 105 https://twitter.com/davrous

↑ Back to topTweet itShare on Facebook

Leave a Reply

Your email address will not be published. Required fields are marked *