How to make a browser add-on using javascript. We are writing our first extension for Chrome. How to package an extension and where to place it

All items in the Chrome Web Store are divided into applications and extensions. We will do both. The application we create will appear as an icon in a new browser tab and will allow you to quickly launch your site. The extension is a special button on the toolbar, clicking on which will cause a panel to appear with the latest site updates.

Building an application for Google Chrome

1. Download the archive with the extension template from this link.

2. Unzip it to any place convenient for you. There is a file inside manifest.json and icon icon.png.

3. Open the file manifest.json in a notepad and edit its contents. You need to enter the name of your site in line 2, its description (up to 132 characters) in line 3, and the site address in lines 5 and 7. Save the changes made.

4. Change the icon from the archive to your own image in PNG format, size 128*128.

Building an extension for Google Chrome

Although the extension is functionally significantly different from the application, its assembly algorithm is not much more complicated.

1. Get the extension template from this link.

2. Unzip. Open the manifest.json file in Notepad and paste the name of your site, its brief description and the title of the extension window (lines 2, 3 and 8).

3. Open the file labnol.js and indicate the address of the RSS feed of your site.

4. Replace the icon from the archive with your own image in PNG format, size 128*128.

Publication

The extension and application we made can be used in two ways. If you are the owner of a website and want to attract additional users to it, you can publish your work in the Chrome Web Store. To do this, pack the extension and add-on files into their own archive, go to the Chrome Dashboard page and upload your work to the Google store. Here you will be asked to upload a screenshot, provide an extended description and specify some other parameters. The page is in Russian, so you can easily understand it. Just note that to publish an extension, you must be the verified owner of the site for which you made the extension. In addition, you will be required to enter a $5 entry fee per publication.

If you collected extensions only for personal use, for example, to conveniently monitor updates to your favorite site, then simply open the extensions page in your browser and enable Developer Mode. Then click the button Download unpacked extension and specify the path to the folder with the files.

Using the proposed method, any website or blog owner will be able to create and place a branded extension for their resource in the Google Chrome browser extensions directory. This will help visitors stay up to date with the latest updates, which will bring additional visitors to your site and increase its popularity.

I have always wanted to tell people about interesting opportunities (technologies) that may now be available to everyone, but for some reason are not available to everyone. Yes, it turned out to be a tautology, but it fully reflects my inner indignation at this burning topic for me. Be that as it may, the talk now will not be about how they say. Today we’ll talk about creating extensions for the Google Chrome browser (hereinafter referred to as Chrome).

The extension that we will be developing throughout this article can be found in the Google Chrome Web Store, with the only difference being that it has advanced functionality. In addition, there is source code on GitHub, again with the caveat that everything there is written in CoffeeScript, and here the story will be written with JavaScript. By the way, I’m not a fan or supporter of CoffeeScript, but it’s quite an interesting and useful thing - I advise you to try it.

If you've ever considered the idea of ​​creating an extension for Chrome, Firefox, Maxthon and other browsers, then you've probably already noticed that the minimum amount of effort you need to put in is for Chrome. You can verify this by looking at the documentation for the corresponding browsers.

Formulation of the problem

Writing an extension begins with its description and setting the tasks that it will solve. Since I am my own boss and I can miss deadlines as many times as I want, I don’t need to write technical specifications - it’s enough to understand that:

  • The extension should hide all comments on the VK social network;
  • The extension must be able to display comments;
  • The extension must be able to display comments on specific pages;

At first glance, everything is simple and we can do it. However, within the framework of the article we will implement only the first two points.

I foresee questions whose content might be something like this: “Why hide comments if this is the whole point of a social network?!” Well, it’s a fair question that deserves a detailed answer:

The circumstances have turned out that lately, when I see comments on VK, I want to give mountains of facepalms to the commentators. I subscribe to a large number of different public pages, thematic (web development) and not so much. And no matter how strange it may seem, I become the most generous in groups with content that is interesting to me, and not cats (in my case, pandas). I have never seen such an amount of unprofessional and ugly information flow in the comments anywhere else, and they even think about arguing. In addition, comments in the news feed do not look aesthetically pleasing. In general, said and done.

Expansion frame

The most pleasant surprise for me was that at the very beginning of the journey we are greeted by the descriptive nature of the actions. Simply put, we need to describe our application, its rights and capabilities - the manifest.json file is used for this.

First of all, you need to fill out three required fields:

( "manifest_version": 2, // Starting with Chrome 18 set 2, otherwise 1 (such old stuff is not supported) "name": "My Extension", // Extension name "version": "versionString" // Extension version )

If everything is clear with the name, and the manifest version is even simpler, then you need to take a closer look at the extension version.

So, we are all used to the fact that a version of something consists of three numbers separated by dots - Major.Minor.Patch (Meaning number). With npm, bower and other delights the conversation is short: either this way or not at all. But Google offers the following options:

"version": "1" "version": "1.0" "version": "2.10.2" "version": "3.1.2.4567"

More information about all the fields in the manifest file can be found in the documentation.

In our case, the manifest file will look like this:

( "manifest_version": 2, "name": "__MSG_app_name__", "short_name": "VKCommentBlocker", "description": "__MSG_app_description__", "version": "0.1.0", "default_locale": "ru", " permissions": [ "activeTab" ], "browser_action": ( "default_icon": "icon_16.png", "default_title": "__MSG_browser_action__" ), "icons": ( "16": "icon_16.png", "48 ": "icon_48.png", "128": "icon_128.png" ), "background": ( "persistent": false, "page": "background.html" ), "content_scripts": [ ( "matches" : [ "http://vk.com/*", "https://vk.com/*" ], "css": [ "styles/commentblocker.css" ] ) ], "web_accessible_resources": [ "styles /commentblocker_on.css" ] )

From what has not been considered before

  • __MSG_key__ is Chrome's take on application internationalization (i18n). Can be used both in the manifest file and in other files (even CSS).
  • web_accessible_resources - an array of resource paths that will subsequently be used in the context of web pages. Without specifying the path in it, nothing can be used on website pages if such behavior is expected.
Expansion Resources

A huge plus for Chrome karma is that we can now enable the extension, of course, if all the resources specified in manifest.json have been created.

I don't think it's worth focusing on what's in the commentblocker.css and commentblocker_on.css files. I will give only the first one, which lists all the selectors that contain comments:

@charset "utf-8"; .wall_module .reply_link_wrap .reply_link ( visibility: hidden !important; ) .wall_module .replies_wrap, #wl_replies_wrap, #wl_reply_form_wrap, #mv_comments_wrap, #mv_your_comment, #pv_comments, #pv_comments_header, #pv_your_comment ( display: none !import ant; visibility: hidden ! important; ) body:after ( position: fixed; content: "__MSG_mode_enable__"; top: 5px; right: 5px; padding: 6px 12px; background-color: #ffc; border: 1px solid #ddd; z-index: 9999; )

In the commentblocker_on.css file, as you might guess, the opposite is true. Note that right in the CSS I'm using a line with the language key content: "__MSG_mode_enable__" . It's time to create a file where these keys will be stored.

In the root of our extension, we create the _locales directory and the en and ru directories nested within it. Next, we describe our keys in the messages.json file.

( "app_name": ( "message": "VK Comment Blocker" ), "app_description": ( "message": "A convenient way to hide comments in the news feed and groups." ), "browser_action": ( "message": " Switch comment type" ), "mode_enable": ( "message": "No comments!" ), "mode_disable": ( "message": "With comments!" ) )

In addition to the message field, there are other fields that you can learn about from the documentation.

Now we create the background.html files, first like this:

Background

Everything here is the same as in regular HTML - nothing unusual. By the way, you don’t have to create the background.html file, since it is generated automatically based on the fields in manifest.json .

Launching the extension

You can run the extension without writing a single line of JavaScript. To do this, you need to go through the menu:

  • Setting up and managing Google Chrome (Hamburger)
  • Additional tools
  • Extensions
  • Check the box next to “developer mode”
  • Download unpacked extension
  • Select folder with extension

The extension loaded and appeared in the menu. Yes, yes, this is this “B”.

It would seem that the extension we just created has nothing in its head (there is no logic), and all comments on the pages of the social network with the letter “B” are now hidden. The answer lies in manifest.json, where in the "content_scripts": () field we indicated on which pages (http://vk.com/* and https://vk.com/*) the commentblocker.css file will be automatically included, which hides all comments. I advise you to read more about mathes patterns. It’s just so simple in appearance, but under the hood it’s almost a gray gelding, with all the bells and whistles.

So, without writing a single line of code, we already have an extension that performs the main task assigned to it.

Reviving the expansion

It remains to complete the second point of the task, namely to implement the ability to display comments. In short, we need to push a commentblocker_on.css file, which will override the rules of the commentblocker.css file. And here our almighty JavaScript rushes to our aid.

Remember what I said about background.html? Yes, yes, about the fact that it doesn’t have to be created. Let's change manifest.json slightly:

... "background": ( "persistent": false, "scripts": [ "scripts/commentblocker.js" ] ), ...

Just connected the JavaScript file. Nothing special. Let's go to this file.

You can’t just stuff JS into a page. And the same problem exists not only with scripts. Therefore, we need to use a special executeScript injection.

First you need to add a click event handler to the extension icon:

Chrome.browserAction.onClicked.addListener(function(tab) ( chrome.tabs.executeScript(tab.id, ( code: "(" + toggleComments.toString() + ")();" )); ));

Where toggleComments is a function that will inject our CSS file into the page:

Var toggleComments = function() ( var extensionLink; (document.getElementById("extension") == null) ? (extensionLink = document.createElement("link"), extensionLink.href = chrome.extension.getURL("/styles/ commentblocker_on.css"), extensionLink.id = "extension", extensionLink.type = "text/css", extensionLink.rel = "stylesheet", document.getElementsByTagName("head").appendChild(extensionLink)) : (document. getElementsByTagName("head").removeChild(document.getElementById("extension"))) );

I think that the words that this piece of code checks whether our CSS is connected on the page and draws conclusions about the need to enable or disable it will be enough.

By the way, there are not many events available that cover a different range of needs. For example, there are such events:

  • onCreated - creating a tab.
  • onUpdated - updating the tab.
  • onRemoved - closing a tab.

It's worth noting that the onUpdated event is called twice:

  • Page update;

On StackOverflow they advise checking the page status:

Chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) ( if (changeInfo && changeInfo.status === "complete") ( ... ) ));

Now, when you click on the icon, a style file will be connected that will display comments, and clicking on the icon again will hide them again.

conclusions

By the way, it is worth mentioning my full version of the VK Comment Blocker extension, which is available in

The most authoritative people in this area are Wladimir Palant (he wrote AdBlock Plus), ... Extensions for Chrome are easier to write than for Firefox. Interestingly, the most popular extension for Chrome is “Tyuryaga VKontakte” (according to Yandex statistics).

The challenge is to develop an extension that will respond to certain HTML pages being opened in the browser. For example, modify the HTML code of a site page to make it more convenient to use this site.
The solution was tested on Google Chrome 24.x and Chromium 6.x (Debian 6.0.6, amd64).

The solution of the problem

Summary of the main questions that arise during the writing of the crx extension.

  • How to register an extension?
  • How to set a timer?
  • How to register an extension?

    For a minimum extension, 4 files are enough:

    128.png background.js content.js manifest.json

    Where is manifest.json:

    ( "manifest_version": 2, "name": "DomainCheck extension", "version": "0.1", "background": ( "scripts": ["background.js"]), "content_scripts": [ ( "matches ": [ "*://*/*" ], "js": [ "content..png" ) // no web_accessible_resources )

    The background.js file contains the code that is executed when the browser starts. In this script you can “hang” the document content loading handler (document.location.href).

    Chrome itself generates _generated_background_page.html:

    To communicate between background.js and the content script, you can use messages (request/message) and chrome.extension.getBackgroundPage(). See also the architecture overview of Chrome extensions. It says that "A content script is some JavaScript that executes in the context of a page that"s been loaded into the browser".

    Note: It is difficult to debug the background page (background.js) in Chromium, since there is no corresponding. tabs on the "Extensions" page in developer mode.

    How to check the current URL, trim it and calculate the hash?

    How to install a handler on DOMContentLoaded is described on developer.chrome.com. See also documentation about Background Pages (background.js).

    To embed HTML code into a page, we can use the advice from the article on Habré (see there for the mention of the global variable document), but we also need to compare the current URL with the list.

    Is document.location.href deprecated?

    Example from stackoverflow.com of getting the fully qualified domain name:

    var url = "http://www.domain.ru/tmp/file.txt?abc=1&cde=2#123" var getLocation = function(href) ( var l = document.createElement("a"); l. href = href; return l; ); var l = getLocation(url); alert(l.hostname)

    You can select a 2nd level subdomain like this:

    var l = getLocation(url); var d = l.hostname; function cutd(str) ( var re = /.*?\.([\w\d-\u0100-\uffff-\.]+\.[\w\d-\u0100-\uffff-\.]+ )/; return str.replace(re,"$1"); ) alert(cutd(d));

    (see tip on stackoverflow.com).

    sha1.js injection ...

    How to set a timer? See Sample Extensions: Event Page Example, background.js:

    chrome.alarms.create((delayInMinutes: 0.1)); chrome.alarms.onAlarm.addListener(function() ( alert("Time"s up!"); ));

    This bug in Chromium was closed on January 9, 2013, but has not yet appeared in the latest builds for Windows.

    If you still decide to use “modern” alarms, then how to correctly set an alarm timer of an arbitrary duration/period is described on stackoverflow.com.

    For compatibility with older browsers, it is better to use window.setInterval() in background.js:

    var i = 0; window.setInterval(function() ( alert(i); i++), 2*1000); // in milliseconds

    The setTimeout() function is a single "alarm clock".

    Disable-Enable chrome extensions launches background.js in a new way. *) do I need to check the same thing to enter the mode? and sleep.

    How to download config.xml/time.txt, and how to parse them?

    If you simply download via XMLHttpRequest, you may get the following message: “XMLHttpRequest cannot load http://site/config.xml. Origin http://www.google.ru is not allowed by Access-Control-Allow-Origin.”. This means that when making a cross-domain request, you did not enable the option for CORS, .htaccess:

    Header set Access-Control-Allow-Origin "*"

    You can allow cross-domain requests in the Chrome extension and through permissions in manifest.json:

    ( "manifest_version": 2, ... "permissions": [ "http://site/" ],

    However, both of these cases do not work for unpacked Chromium extensions. For packaged extensions, Chromium versions 6.0-7.0 sometimes displays "Bad magic number" (for pure Chrome this error is not observed).
    Note: CORS protection can be bypassed in Chromium via the --disable-web-security option:

    $ chromium-browser --disable-web-security http://domain/path

    Addition: for Chromium you need to change the lines in manifest.json to the following (explicitly specifying the domain and adding an asterisk in the path):

    ( "permissions": [ "http://site/*" ],

    To prevent the web inspector(?) in chrome from complaining about XMLHttpRequest() when the Internet is turned off, you can do this:

    var req = null; try ( req = new XMLHttpRequest(); ) catch(err) () // see advice at stackoverflow.com

    (this is written on advice from stackoverflow.com). ...

    To debug parsing, you can use console.log("line"). Strings accept carriage returns using "\n".

    Parsing XML into JavaScript in the chrome extension is done like this:

    var xml = req.responseXML.documentElement; var ts = xml.getElementsByTagName("timeout"); var timeout = ts.textContent; if (ts) (console.log("timeout="+timeout);) var ds = xml.getElementsByTagName("domain"); if (ds) ( for (var i = 0; i< ds.length; i++) { console.log("domain: "+ds[i].textContent); } }

    How to use global atomic variables and table data in chrome extension?

    To synchronize extension data using the Storage API, you need Chrome version >= 20. In the manifest (manifest.json) you need to write the following:

    "premissions": ["storage" ]

    As they write in the google group "Chromium HTML5", "I turned my attention to Web SQL Database but it seems Web SQL is no longer in "active maintenance" which leads me to believe that it will be dropped from HTML spec." See W3C Web SQL Database, note for more details. You can try using Basic concepts - to expand the space for the database, you can use permissions: unlimitedStorage in manifest.json. To use "Unlimited storage" there are the following Offline APIs: 1) App Cache; 2) File System; 3) IndexedDB; 4) WebSQL (deprecated). For an example of using IndexedDB in Chrome, see. For an example of working with IndexedDB, see gist.github.com:

    window.indexedDB = window.indexedDB || window.webkitIndexedDB; var req = indexedDB.open("my db") req.onerror = function() ( console.log("error"); )

    IndexedDB file locations,
    Windows: C:\Users\\AppData\Local\Google\Chrome\User Data\Default\IndexedDB,
    Linux: /home//.config/google-chrome/Default/IndexedDB/chrome-xxx.indexeddb.leveldb/:

    $ sudo ls -la /home/anonymous/.config/google-chrome/Default/IndexedDB/chrome-extension_ojeihbjghbabiocoglbfhdebhhckdnol_0.indexeddb.leveldb/ total 24 drwx------ 2 anonymous anonymous 4096 Feb 7 03:08 . drwx------ 3 anonymous anonymous 4096 Feb 7 03:08 .. -rw-r--r-- 1 anonymous anonymous 285 Feb 7 03:08 000003.log -rw-r--r-- 1 anonymous anonymous 16 Feb 7 03:08 CURRENT -rw------- 1 anonymous anonymous 0 Feb 7 03:08 LOCK -rw-r--r-- 1 anonymous anonymous 46 Feb 7 03:08 LOG -rw-r --r-- 1 anonymous anonymous 32 Feb 7 03:08 MANIFEST-000002

    You can look at examples of using IndexedDB on the Mozilla Developer Network.

    Inserting a large number of records into IndexedDB is covered on stackoverflow.com.

    To add items to IndexedDB, you need to use

    indexedDB.db.transaction().objectStore().put())

    In the IndexedDB database folder, old databases are stored as .sst files, and new (current) ones are stored as .log files.

    setVersion() is Deprecated. But there is one trick here [about onupgradeneeded()]: ...

    As stated in Parashuram Narasimhan's blog, "For Chrome: In case of chrome, the onupgradeneeded function is not called. The database"s onsuccess function is called. Here, the existence of the setVersion method is checked. If the method exists, and the specified version is greater than the database version, a the setVersion method is called. The onsuccess of the setVersion"s request call invokes the user"s onupgradeneeded method with the version transaction. Once the method completes, the versionTransaction is committed by closing the database. The database is opened again with the latest version and this is passed to the onsuccess defined by the user." (so to call onupgradeneeded(), I do db.setVersion("3")).

    How to fetch data in content.js:

    dbreq.onupgradeneeded = function(event) ( console.log("dbreq.onupgradeneeded"); var db = event.target.result; var tx = db.transaction(["test_db"], "readonly"); var store = tx.objectStore("todo");

    To open the database, use the code from axemclion+jepp (openReqShim function).

    When we open an IndexedDB database that does not exist, it will be created with a version number, version = 0. In this case, onupgradeneeded() and onsuccess() are called sequentially. The first time we call onupgradeneeded(), version is already = 1. When we open the [existing database] a second time, onupgradeneeded() is no longer called, and the version number = 1. (?does not increase) Only dbreq.onsuccess() is called.

    One more thing. It says that "With Chrome prior to 23 you need to create such a transaction manually by calling setVersion() - an API that has been removed from the spec. The older spec can be found at: http://www.w3. org/TR/2011/WD-IndexedDB-20110419/", that is, in order to avoid the error "NotFoundError: DOM IDBDatabase Exception 8" in the chrome logs, you need to call setVersion().

    In Chromium 6.0.472.63 (59945) the IndexedDB implementation is not stable, so it is disabled and does not work =)

    In general, you need to use background.js + iframe + exchange with content scripts via messages

    How to handle the page opening event?

    "content_scripts": [ ( "matches": [ "*://*/*" ], "js": [ "content.js" ], "run_at": "document_start" ) ], ...

    Content.js:

    document.addEventListener("DOMContentLoaded", function () ( alert("Abc "+document.location.href); ));

    How to format the HTML code of a page? See writing Firefox extensions. How to package an extension and where to place it?

    To place an extension on the Chrome WebStore, you need to pay Google an entry fee of $5 (then you can place any number of extensions). You can pay via VISA, MasterCard, AMEX or DISCOVER (in addition, when paying, you must indicate your full postal address and First and Last Name).

    To place the extension you need a Google account, and so on. You will need a screenshot and an advertising image. You will need to update the extension code manually, through the same Chrome WebStore (as I understand, there is no automatic update by URL, as in Firefox). In the manifest.json file, you need to update the extension version. A few minutes after adding, the extension will be available in the search for Chrome extensions.

    Extension packaging for Linux:

    #!/bin/bash 7z a -tzip ../domainck-chromium.zip ./* mv ../domainck-chromium.zip ../domainck-chromium.crx

    Keywords: Chromium builds for windows HOWTO, Google Chrome download page, Google Chrome sample extensions.

    There are many sites on the Internet that provide the ability to scroll up a page without using a mouse or scroll bar. But at the same time, there are still sites where there is no such implementation. "Why not try to write a script that would add such a button to all sites?" - I thought and set about implementing it. Such scripts are called user scripts and have the extension *.user.js. For example, you can read it on Habré. Unfortunately, it cannot be done without pitfalls. There are some differences in the implementation of userjs for different browsers. I will focus on describing the implementation of our userjs script as an extension for the Google Chrome browser.

    manifest.json

    The file required for the Google Chrome extension is manifest.json which describes parameters, paths to external files (*.js, *.css, etc.), version support, etc. for expansion. You can read more about the file. In our case, the manifest.json file looks like this:

    ( "manifest_version": 2, "content_scripts": [ ( "exclude_globs": , "include_globs": [ "*" ], "js": [ "jquery.js", "backTopUserJS.user.js" ], "css ": [ "css/style.css" ], "matches": [ "http://*/*", "https://*/*" ], "run_at": "document_end" ) ], "converted_from_user_script ": true, "description": "Back top userscript extension for google chrome", "name": "backTopUserJS", "version": "1" )

    For convenience, we use the JQuery library, which we placed next to the manifest.json file and the main script file (in our case, backTopUserJS.user.js). Its contents are as follows:

    // ==UserScript== // @name backTopUserJS // @author Aleksandr Filatov // @license MIT // @version 1.0 // ==/UserScript== function main() ( var disp = $(window).scrollTop () > 400 ? "block" : "none"; var $upButton = $("

    "); $(document).find("body").append($upButton); $upButton.click(function () ( $("html, body").animate(( scrollTop: 0 ), "slow" ); )); $(window).scroll(function () ( if ($(window).scrollTop() > 400) $upButton.fadeIn("slow"); else $upButton.fadeOut("slow"); )); ); var script = document.createElement("script"); script.appendChild(document.createTextNode("("+ main +"))();")); (document.body || document.head | | document.documentElement).appendChild(script);

    I think the script is clear enough to consider in detail. Only the last 3 lines are worth explaining. In fact, this is a small hack that inserts our script into the site page code. If any of you find a better way, you can write your fixes in the comments :)

    Installing the extension in the browser

    You can install custom scripts in Google Chrome, similar to other browsers, but since Google cares about our security, they have to be wrapped in browser extensions. Let's look at the installation step by step.

    Create a folder for our extension like this: C:\MyChromeExtensionUserJS

    For each extension we create our own directory, for example in our case we will call it C:\MyChromeExtensionUserJS\backTopUserJS . Add extension files to this directory.

    Go to "About Google Chrome Browser" -> "Extensions" tab or write chrome://extensions/ in the address bar

    Check the box "Developer mode"

    Click the "Load unpacked extension" button and select the directory of our extension. Click "OK".

    The extension is installed and ready to use. To update extensions after you have made changes to it, simply click the "Update extension" button or, in developer mode, press the keyboard shortcut Ctrl+R.

    Bottom line

    You can view the script sources on github. The script does not claim to be ideal, but serves only as an example and impetus for writing your own custom scripts for Google Chrome.