How to Build a Chrome Extension to Add Barcode Reading Function to Your Web Pages

Chrome extensions are software programs, built on web technologies (such as HTML, CSS, and JavaScript) that enable users to customize the Chrome browsing experience.1

In this article, we are going to write a Chrome extension to add the barcode reading function to web pages with Dynamsoft Barcode Reader.

The extension adds a context menu item for images. The user can right click the image to read barcodes from it. Check out the video to see how it works:

The extension has been published to the Chrome store.

Write a Chrome Extension to Read Barcodes

Let’s do this in steps.

  1. Create a basic manifest file in version 3.

    {
      "name": "Barcode Reader",
      "version": "1.0",
      "manifest_version": 3
    }
    
  2. Download the JavaScript SDK of Dynamsoft Barcode Reader. Copy its dist folder into the root of the extension and make it web_accessible_resources in manifest so that we can access it in web pages.

    "web_accessible_resources": [
      {
        "resources": ["dist/*"],
        "matches": ["<all_urls>"]
      }
    ]
    
  3. Create a content.js file as content script to load the library of Dynamsoft Barcode Reader to the web page.

    init();
    
    async function init(){
      const distURL = new URL(chrome.runtime.getURL("/dist/"));
      await loadLibrary(distURL+"/dbr.js","text/javascript");
    }
    
    function loadLibrary(srcUrl,type,id,data){
      return new Promise(function (resolve, reject) {
        let scriptEle = document.createElement("script");
        scriptEle.setAttribute("type", type);
        scriptEle.setAttribute("src", srcUrl);
        if (id) {
          scriptEle.id = id;
        }
        if (data) {
          for (let key in data) {
            scriptEle.setAttribute(key, data[key]);
          }
        }
        document.body.appendChild(scriptEle);
        scriptEle.addEventListener("load", () => {
          console.log(srcUrl+" loaded")
          resolve(true);
        });
        scriptEle.addEventListener("error", (ev) => {
          console.log("Error on loading "+srcUrl, ev);
          reject(ev);
        });
      });
    }
    

    Modify manifest as well:

    "content_scripts": [
      {
        "matches": ["<all_urls>"],
        "js": ["content.js"],
        "css": ["style.css"]
      }
    ],
    
  4. Define the barcode reading functions in dist/reader.js.

    It allows calling Dynamsoft Barcode Reader to read barcodes from an image and displaying the results in a modal. An image element is created from the src with the crossOrigin set for decoding so that we can get the image data of cross-origin images.

    JavaScript:

    let DBRReader;
    
    async function decode(img) {
      showModal();
      if (!DBRReader) {
        updateStatus("Initializing...");
        DBRReader = await Dynamsoft.DBR.BarcodeReader.createInstance();
      }
      updateStatus("Decoding...");
      let results;
      try {
        results = await DBRReader.decode(img);  
      } catch (error) {
        updateStatus("Error "+error);
        return;
      }
      updateStatus("Found "+results.length+((results.length>1)?" barcodes.":" barcode."));
      if (results.length > 0) {
        const resultList = document.querySelector(".dbr-results");
        resultList.innerHTML = "";
        for (let index = 0; index < results.length; index++) {
          const result = results[index];
          const item = document.createElement("li");
          item.innerText = result.barcodeFormatString + ": " + result.barcodeText;
          resultList.appendChild(item);
        }
      }
    }
    
    function updateStatus(text){
      let status = document.querySelector(".dbr-status");
      if (status) {
        status.innerText = text;
      }
    }
    
    function showModal(){
      let modal = document.createElement("div");
      modal.className = "dbr-modal";
      let status = document.createElement("div");
      status.className = "dbr-status";
      let resultList = document.createElement("ol");
      resultList.className = "dbr-results";
      let closeBtn = document.createElement("button");
      closeBtn.innerText = "Close";
      closeBtn.addEventListener("click", () => {
        removeModal();
      });
      modal.appendChild(status);
      modal.appendChild(resultList);
      modal.appendChild(closeBtn);
      document.body.appendChild(modal);
    }
    
    function removeModal(){
      document.querySelector(".dbr-modal").remove();
    }
    
    function decodeFromSrc(src) {
      if (src) {
        let img = document.createElement("img");
        img.crossOrigin = "Anonymous";
        img.src = src;  
        img.onerror = function (e) {
          showModal();
          updateStatus("Unable to load the image");
        }
        img.onload = async function() {
          await decode(img);
          document.getElementById("dbr").remove();
        }
      }
    }
    

    CSS:

    .dbr-modal {
      position: fixed;
      left: 50%;
      top: 50px;
      max-width: 80%;
      transform: translateX(-50%);
      z-index: 999999;
      background: #fff;
      padding: 20px;
      border: thick double black;
      border-radius: 5px;
      text-align: center;
      font-size: 16px;
    }
    
    .dbr-results {
      overflow: auto;
      max-height: 250px;
    }
    
    .dbr-results li {
      list-style: decimal;
      white-space: nowrap;
    }
    

    Inject it in content.js:

    await loadLibrary(distURL+"/reader.js","text/javascript");
    
  5. Add a context menu item for images so that users can right click images to read barcodes.

    1. Add the contextMenus and activeTab permissions in the manifest.

      "permissions": ["activeTab","contextMenus"],
      
    2. Create a background.js to create the context menu and listen for events.

      chrome.runtime.onInstalled.addListener(() => {
        chrome.contextMenus.create({
          "id": "read-barcodes-menu",
          "title": "Read barcodes from the image",
          "contexts": ["image"]
        });
      });
      
      chrome.contextMenus.onClicked.addListener(function (info, tab) {
        chrome.tabs.sendMessage(tab.id, {info:info}, function(response) {
        console.log(response.farewell);
        });
      });
      

      Define it as service worker in the manifest:

      "background": {
        "service_worker": "background.js"
      },
      
    3. In content.js, we can receive the messages sent from the service worker.

      chrome.runtime.onMessage.addListener(
        function(request, sender, sendResponse) {
          if (request.info.srcUrl) {
            const distURL = new URL(chrome.runtime.getURL("/dist/"));
            loadLibrary(distURL+"/read.js","text/javascript","dbr",{imgSrc:request.info.srcUrl});
          }
        }
      );
      

      The dist/read.js is used for sending messages from the content script to the web page:

      const dbrEle = document.getElementById("dbr")
      const src = dbrEle.getAttribute("imgSrc");
      decodeFromSrc(src);
      
  6. Add an options page for setting the license and runtime settings (in JSON template) of Dynamsoft Barcode Reader.

    License: By default, a one-day public trial license will be used. You can apply for your own license here and set it in the options page.

    Runtime settings: Dynamsoft Barcode Reader provides rich image processing parameters to adjust for different use cases like curved barcodes and blurry barcodes. We can modify it with a JSON template.

    Steps:

    Create options.html and options.js. Define the options item in the manifest and add the storage permission for writing the settings.

    "permissions": ["storage","activeTab","contextMenus"],
    "options_page": "options.html",
    

    HTML:

    <!DOCTYPE html>
    <html>
    <head><title>Options</title></head>
    <body>
      <label>License:</label>
      <input type="text" id="dbrLicense"/>
      <div>
        <a href="https://www.dynamsoft.com/customer/license/trialLicense?product=dbr&source=codepool">Apply for a license.</a>
      </div>
      <label>Template (<a href="https://www.dynamsoft.com/barcode-reader/docs/core/programming/features/use-runtimesettings-or-templates.html?lang=js&&ver=latest#json-templates">docs</a>):</label>
      <div>
        <textarea>
        </textarea>
      </div>
      <button id="save">Save</button>
      <script src="options.js"></script>
    </body>
    </html>
    

    JavaScript:

    function save() {
      const dbrLicense = document.getElementById("dbrLicense");
      const template = document.querySelector("textarea");
      chrome.storage.sync.set({
        dbrLicense: dbrLicense.value,
        dbrTemplate: template.value
      }, function() {
        // Update status to let user know options were saved.
        alert("saved");
      });
    }
    
    function load() {
      chrome.storage.sync.get({
        dbrLicense: '',
        dbrTemplate: ''
      }, function(items) {
        if (items.dbrLicense) {
          document.getElementById("dbrLicense").value = items.dbrLicense;
        }
        if (items.dbrTemplate) {
          document.querySelector("textarea").value = items.dbrTemplate;
        }
      });
    }
    
    document.getElementById("save").addEventListener("click", () => {
      save();
    });
    document.addEventListener('DOMContentLoaded', load);
    
  7. Use the license and template.

    1. Update the reader.js to update runtime settings.

      let previousDBRTemplate = "";
      
      async function updateRuntimeSettings(template){
        if (template != undefined) {
          if (previousDBRTemplate != template) {
            if (template === "") {
              await DBRReader.resetRuntimeSettings();
            }else{
              await DBRReader.initRuntimeSettingsWithString(template);
            }
            previousDBRTemplate = template;
          }
        }
      }
            
      async function decode(img, template) {
        //...
        updateRuntimeSettings(template);
      }
            
      function decodeFromSrc(src, template) {
        //...
        decode(img, template);
      }
      
    2. Update content.js to get the template and license from the storage.

      async function init(){
        const distURL = new URL(chrome.runtime.getURL("/dist/"));
        chrome.storage.sync.get({
          dbrLicense: ''
        }, async function(items) {
          await loadLibrary(distURL+"/dbr.js","text/javascript","",{"data-license":items.dbrLicense}); //use the 'data-license' attribute to set the license
          await loadLibrary(distURL+"/reader.js","text/javascript");
        });
      }
            
      chrome.runtime.onMessage.addListener(
        function(request, sender, sendResponse) {
          if (request.info.srcUrl) {
            const distURL = new URL(chrome.runtime.getURL("/dist/"));
            chrome.storage.sync.get({
              dbrTemplate: ''
            }, async function(items) {
              loadLibrary(distURL+"/read.js","text/javascript","dbr",{imgSrc:request.info.srcUrl,template:items.dbrTemplate}); //pass the template as an attribute
            });
          }
        }
      );
      
    3. Update read.js to use the template attribute.

      const dbrEle = document.getElementById("dbr")
      const src = dbrEle.getAttribute("imgSrc");
      const template = dbrEle.getAttribute("template");
      decodeFromSrc(src,template);
      
  8. Namespace protection. Since the extension injects scripts into other web pages, we need to isolate our code in reader.js in an object like the following:

    let DBRChromeExtension = {
      DBRReader:undefined,
      previousDBRTemplate:"",
      decode:async function(img, template) {},
      showModal: function() {}
      //...
    }
    

All right, we’ve now finished writing the Chrome extension.

Source Code

Get the source code of the extension to have a try:

https://github.com/tony-xlh/barcode-reader-chrome-extension/

References