Monday 17 January 2022

Running JavaScript Before Any Other Scripts On A Page

Injecting A Script At The Top Of Your HTML Page


By Strictly-Software

If you are developing an extension for either Firefox, Opera or Chrome, Brave, Edge, and Chromium, then you might come across the need to be able to inject some code into the top of your HTML page so that it runs before any other code.

When developing extensions for Chromium based browsers such as Chrome, Brave and Edge, you will most likely do this from your content.js file which is one of the main files that holds code to be run at certain stages of a pages lifetime.

As the Chrome Knowledge Base says the property values for the "run_at" property include;

  • document_idle, the preferred setting where scripts are guaranteed to run after the DOM is complete and after the window.onload event has loaded all resources which can include other scripts.
  • document_end, content.js scripts are injected immediately after the DOM is complete, but before subresources like images and frames have loaded. So after the DOM is loaded but before window.onload has finished loading external resources.
  • document_start, ensures scripts are injected after any CSS files are loaded but before any other DOM is constructed or any other script is run.  
  • There is of course the "run_at" property in the manifest.json file, which can be set to "document_start", to enable the codes running before other code does. This is especially useful if you need to change header values before a web page loads so that modified values such as the Referer or User-Agent can be modified. However, there may also be a need for you to set up an object or variable that is inserted into the DOM for code within the HTML page to access.

    For example in a User-Agent switcher where you need to both overwrite the Navigator object in JavaScript and the Request header, you may want to create an object or variable that holds the original REAL navigator object or its user-agent so that any page you may create yourself or offer to your users the ability to see the REAL user-agent, browser, language, and other properties if they wanted to.

    For example, I have my own page that I use to show me the current user-agent and list the navigator properties

    However, if they have been modified by my own user-agent switcher extension I also offer up a variable holding the original REAL user-agent so that it can be shown and compared with the spoofed version to see what has changed. I also have a variable that holds the original navigator object in case I want to look at the properties.

    Therefore my HTML page may want to inspect this object if it exists with some code on the page.

    // check to see if I can access the original Navigator object and the user agent string
    if(typeof(origNavigator) !== 'undefined' && origUserAgent !== null)
    {
    	// get the real Browser name with my Detect Browser function using the original Navigator user-agent
    	let realBrowser = Browser.DetectBrowser(origUserAgent);
    
    	// output on the page in a DIV I have
    	G("#RealBrowser").innerHTML = "<b>Real Browser: " + realBrowser "</b>";
    }

    This code just uses a generic Browser detection function for taking a user-agent and finding the Browser name. It even detects Brave by ruling out other browsers and if it is Chrome at the end I check for the Brave properties or mention of the word in the string, which they used to have but newer versions have removed it. 

    However there is hope in the community that they will create a unique user-agent with the word Brave in as at the moment people are having to do object detection which is the better method, and as Brave tries to hide, there are plenty of query strings and other API calls which can be made to find out whether the result indicates Brave rather than Chrome.

    However, at the moment, I am just using a simple detection on the window.navigator object that if TRUE indicates that it is actually Brave NOT Chrome. 

    A later article shows a longer function I developed with fallbacks in case the objects do not exist anymore as there used to be a brave object on the window e.g window.brave that no longer exists, so did many objects for Chrome such as window.google and window.googletag that no longer exist. However, this article explains all that.

    This is just the one line test you can do, it ensures it is not FireFox with a test for a Mozilla only object window.mozInnerScreenX and then checks that it is a Chromium browser with tests for window.chrome and that it's also webkit with a test for window.webkitStorageInfo before some tests for navigator.brave and navigator.brave.isBrave to ensure it's Brave not Chrome e.g:

    // ensure that a Chrome user-agent is not actually Brave by checking some properties that seem to work to identify the browser at the moment anyway...
    let isBrave = !("mozInnerScreenX" in window) && ("chrome" in window && "webkitStorageInfo" in window && "brave" in navigator && "isBrave" in navigator.brave) ? true : false


    However, this article is more about injecting a script into the HEAD of your HTML so that code on the page can access any properties within it.

    As my extension offers an Original Navigator object and a string holding the original/real user-agent before I overwrite it, then I want this code to be the first piece of JavaScript on the page.

    This doesn't have to be limited to extensions and you may have code you want to inject in the HEAD when the DOMLoads before any other code.

    This is a function I wrote that attempts to place a string holding your JavaScript into a new Script block I create on the fly and then insert before any other SCRIPT in the document.head.

    However, if the page is malformed, or has no defined head area it falls back to just appending the script to the document.documentElement object.

    If you pass false in for the 2nd parameter which tells the function whether or not to remove the script after inserting it then if you view the generated source code for the page you will see the injected script code in the DOM.

    The code looks within the HEAD for another script block and if found it inserts it before the first one using insertBefore() however if there is NO script block in the HEAD then the function will just insert the script into the HEAD anyway using the appendChild() method.

    An example of the function in action with a simple bit of JavaScript that stores the original navigator object and user-agent is below. You might find multiple uses for such code in your own work.

    // store the JavaScript in a string
    var code = 'var origNavigator = window.navigator; var origUserAgent = origNavigator.userAgent;";
    
    // now call my function that will append the script in the head before any other and then remove it if required. For testing you may want to not remove it so you can view it in the generated DOM.
    appendScript(code,true);
    
    // function to append a script first in the DOM in the HEAD, with a true/false parameter that determines whether to remove it after sppending it.
    function appendScript(s,r=true){
    
    	// build script element up
    	var script = document.createElement('script');
    	script.type = 'text/javascript';
    	script.textContent = s;
    	
    	// we want our script to run 1st incase the page contains another script e.g we want our code that stores the orig navigator to run before we overwrite it
    
    	// check page has a head as it might be old badly written HTML
    	if(typeof(document.head) !== 'undefined' && document.head !== null)
    	{	
    		// get a reference to the document.head and also to any first script in the head
    		let head = document.head;
    		let scriptone = document.head.getElementsByTagName('script')[0];
    
    		// if a script exists then insert it before 1st one so we dont have code referencing navigator before we can overwrite it		
    		if(typeof(scriptone) !== 'undefined' && scriptone !== null){	
    			// add our script before the first script
    			head.insertBefore(script, scriptone);
    		// if no script exists then we insert it at the end of the head
    		}else{
    			// no script so just append to the HEAD object
    			document.head.appendChild(script);
    		}
    	// no HEAD so fall back to appending the code to the document.documentElement
    	}else{
    		// fallback for old HTML just append at end of document and hope no navigator reference is made before this runs
    		document.documentElement.appendChild(script);
    	}
    	// do we remove the script from the DOM
    	if(r){
    		// if so remove the script from the DOM
    		script.remove();
    	}
    }
    



    I find this function very useful for both writing extensions and also when I need to inject code on the fly and ensure it runs before any other scripts by using a onDOMLoad method.

    Let me know of any uses you find for it.

    Useful Resource: Content Scripts for Chrome Extension Development. 


    By Strictly-Software

    Friday 7 January 2022

    Make Your First "Hello World" Brave / Chrome / Edge Extension

    Making your 1st Chrome / Brave Extension....

    By Strictly-Software

    This article is about how to go ahead and make your first extension for  Brave, Chrome, or Edge. As I no longer use Chrome but use Brave instead which like Microsoft Edge is based on the core Chromium Open Source standards-compliant browser, any mention of Brave in this article can be applied to Chrome or Edge as well.

    The good thing about choosing to write your first extension for a Chromium-based browser is that once you have done it, it can be applied to 3 major browsers, Chrome, Brave, and Microsoft's replacement for Internet Explorer, Edge. So if you want to maximize your efforts choosing to develop an extension for Chromium gives you much more scope to apply it to other major browsers unlike Mozilla for Firefox. 

    Not only do I like Brave for paying me not any site owner, to watch adverts in cryptocurrency (BAT), it is more secure, with an easy way to allow or prevent tracking cookies, 3rd party cookies, scripts, adverts, popups, and more from following you around the web.

    It does this by stripping out all the tracking codes by default before rendering the page.

    By doing this it also saves bandwidth and therefore time. It also uses TOR as it's an incognito window so you are more secure when trying to avoid people by going through the onion to access many pages that Brave will sometimes automatically redirect you to.

    You can download the Brave Browser from this link and start to strip trackers and earn cryptocurrency for ignoring tiny little adverts that are shown to you in the corner of the screen right now.


    Setting Up Your Extension

    1. Create a folder somewhere on your machine where all the files for the extension will be located. I just created a folder in documents called "Extensions" and then within it for this test a "HelloWorld" sub folder.

    2. Create a manifest.json file with basic info about your extension. The manifest.json file tells Chrome important information about your extension, like its name and which permissions it needs.

    The manifest version should always be 2, because version 1 is unsupported as of January 2014. So far our extension does absolutely nothing, but let’s load it into Chrome / Brave anyway.

    Let's add some info into it which describes the extension.

    {
      "manifest_version": 2,
      "description": "Hello World Extension",
      "name": "Strictly-Software Hello World Extension",
      "version": "0.1.1"
    }

    3. Create a content script which is "a JavaScript file that runs in the context of web pages." This means that a content script can interact with web pages that the browser visits.

    So open a text file and make the script, as we are doing a hello world all we need to do is something simple such as showing an alert box whenever we go to a new URL that shows a message e.g:

    
    // content.js
    alert("Hello from Strictly-Softwares Brave/Chrome extension!");
    

    Save the file in the same folder as all of your other files as content.js

    As we want the alert box to show on every URL we access we need to add a bit of script in the manifest file which tells it to act on <all_urls> you can see this in the below example as we also tell the manifest.json file about our JavaScript that needs to run on all the URLS we access.

    {
      "manifest_version": 2,
      "description": "Hello World Extension",
      "name": "Strictly-Software Hello World Extension",
      "version": "0.1.2022"
      "content_scripts": [
        {
          "matches": [
            "<all_urls>"
          ],
          "js": ["content.js"]
        }
      ]
    }

    If we wanted the extension code to only run on certain pages then we can use regular expression like syntax which you can explore later to define the URLS that our code acts on.

    Also if we were loading multiple JS files into our manifest file, we would use commas within the square brackets to denote them e.g to add jQuery go and get the version of the plugin you want e.g from https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js and save a local copy into your extension folder. 

    There is no point loading it from a remote site each time as it takes time to load, plus they have stopped using the "latest" version in their filename which you used to be able to reference to get the most up to date version from Google's or jQuery's CDN, however they stopped it due to the amount of requests they were getting which was almost on an unintentional DOS scale.

    For framework files you should always get a version that you know works, and save it locally or on a CDN, so that it can be cached and not loaded from a remote site each time. Also there is no need to keep updating it to the next version when one comes out. If it works for the project you are working on then leave the script reference alone. 

    This is something many developers didn't understand by always loading in the latest version remotely, this took network calls, time to load, and it often introduced new bugs into their code. It is far better to store all your code locally so it can be cached and so that you know it works. So to add jQuery into the extension we would do this.

    "js": ["jquery-3.3.1.js", "content.js"]

    The manifest would look like this if you did add extra files in e.g:

    {
      "manifest_version": 2,
      "description": "Hello World Extension",
      "name": "Strictly-Software UserAgent Extension",
      "version": "0.1.2022",
      "content_scripts": [
        {
          "matches": [
            "<all_urls>"
          ],
          "js": ["jquery-3.3.1.js", "lightbox.js", "scripts.js", "content.js"]
        }
      ]
    }

    4. Add a logo for your extension at the size of 24x24 this will appear in your toolbar when you pin it so you can use the extension. At the same time make some more icons that will be used on the brave://extensions/ page when loading up your extension, for example when you click on it to see the details (Name, description, version etc), and elsewhere.

    You can use this page to convert an image, screenshot, logo into the sizes you need > https://icoconvert.com/.

    The icon for the toolbar should be called just icon.png which you can put in the same folder as the other images which you should name with the size on to not get confused. 

    At the same time make .png logos of the sizes 16x16, 48x48 and 128x128. These are put at the top level in the manifest as you can see below as they are used in different places. The toolbar icon is down at the bottom as you can see.

    So the final manifest file will look like this.

    {
      "manifest_version": 2,
      "description": "Hello World Extension",
      "name": "Strictly-Software UserAgent Extension",
      "version": "0.1.2022",
      "icons": {
        "16": "icon16.png",
        "48": "icon48.png",
        "128": "icon128.png"
      },
      "content_scripts": [
        {
          "matches": [
            "<all_urls>"
          ],
          "js": ["content.js"]
        }
      ],
      "browser_action": {
        "default_icon": "icon.png"
      }
    }

    5. Now in Brave/Chrome go to extensions and open it up

    In Brave it will be a URL like brave://extensions/ and Chrome chrome://extensions/. In the top right corner turn developer mode on. Then in the top left corner hit the "Load Unpacked" button and select your folder containing all your files and images.

    You will see your extension appear in the list of already loaded extensions with one of your logos showing e.g:


    5. Clicking on "Details" will show the information from your manifest and a different size logo e.g:


    6. Now select the extension icon in the toolbar to get up your drop down menu of extensions and select the pin so it's pinned to your toolbar e.g:


    7. Now try going to any URL and you should get an alert box pop up straight away with the message you put in the content.js file. Even before other JavaScript loaded content fires from a DOM or Window onload event, this alert should fire straight away e.g:


    8. And there you go, your first Chrome/Brave extension. Obviously, this just a Hello World test showing you how you can create a basic extension and if you are going to make an extension you will need to read up more on all the different features and actions possible.

    However, this is a good guide for someone wondering about how to make an extension or insert code that fires before the page is loaded

    I have an idea for what I need to use this for such as overwriting JavaScript objects and putting my own properties into them so that they load when the page does before any other JavaScript code as well as being accessible to any local code that tries to access them. You can read a bit about this in a later article, injecting a script at the top of your HTML page.

    If you want more information about what you can do just head to the main Chrome extensions page for developers at https://developer.chrome.com/docs/extensions.

    Have fun...

    By Strictly-Software