Getting started with chrome plug-in development - nanny level strategy

Here is a message

Manifest version 2 is deprecated, and support will be removed in 2023. See https://developer.chrome.com/blog/mv2-transition/ for more details.

The MV2 version of the chrome plugin will stop supporting in 2023

What files and folders should the chrome plugin contain

D:.
│  manifest.json
├─html
│      index.html
├─images
│      icon-128.png
│      icon-16.png
├─scripts
│      background.js
├─styles
│      main.css
└─_locales
    ├─en
    │      messages.json
    └─zh_CN
            messages.json
  1. html: store html pages
  2. images: store plugin icons
  3. scripts: store js files
  4. styles: storage style
  5. _locales: store multilingual files
  6. manifest.json: used to configure all plugin-related configurations, as a chrome entry file, it must be placed in the root directory ( 必须存在)

analyze

  1. The directory structure is like a web page, which is essentially a website application, a webapp
  2. Compared with ordinary webapp, more browser-level APIs can be called, including counting money, history records, network request interception, interception of user input, etc.

Important Configuration Notes

manifest.json

For additional configuration, see https://blog.csdn.net/sysuzjz/article/details/51648163

{
    
    
    "manifest_version": 3, // 清单版本号,建议使用 版本 3,版本 1和2 是旧的,已弃用,不建议使用
    "name": "first-test-plugin", // 插件名称
    "version": "0.0.1", // 插件版本
    "description": "这里是第一个测试插件", // 描述,可写可不写
    "icons":
    {
    
    
        "16": "images/custom/16x16.png",
        "48": "images/custom/48x48.png",
        "128": "images/custom/128x128.png"
    },
    // !!!browser_action和page_action只能添加一个
    "browser_action": //浏览器级别行为,所有页面均生效
    {
    
    
        "default_icon": "images/custom/16x16.png", // 图标的图片
        "default_title": "Hello lanfengqiuqian", // 鼠标移到图标显示的文字
        "default_popup": "html/popup.html" // 单击图标后弹窗页面
    },
    "page_action": //页面级别的行为,只在特定页面下生效
    {
    
    
        "default_icon":
        {
    
    
            "24": "images/custom/24x24.png",
            "38": "images/custom/38x38.png"
        },
        "default_popup": "html/popup.html",
        "default_title": "Hello lanfengqiuqian"
    },
    "author": "lanfengqiuqian", // 可选填写
    "automation": false, // 是否开启自动化
    "background": // 背景页的脚本路径,一般为插件目录的相对地址
    {
    
    
        "scripts": [
            "scripts/background.js",
            "scripts/devtools-page.js"
        ]
    },
    "devtools_page": "html/devtools-page.html", // 在开发工具中的页面
    "content_scripts": [ // 内容脚本一般植入会被植入到页面中, 并且可以控制页面中的dom
        {
    
    
            "js": ["js/else-insert.js"], // 这里面的数组都是可以放多个的
            "css": ["css/else-insert.css"],
            "matches": ["<all_urls>"] // 被植入到页面,只在这些站点下 content_scripts会运行
        }
    ],
    "permissions": [ // 安装的时候提示㤇的权限
        "cookies", // 使用cookies
        "webRequest", // 使用web请求
        "http://*", // 可以通过executeScript或者insertCSS访问的网站地址。如: https://*.google.com/
        "management", //
        "storage", // 使用本地存储
        "tabs", // 操作标签
        "contextMenus" //右键菜单
    ]
    "default_locale ": "zh_CN" //默认语言(比如"zh_CN")
}

Start a plugin

Preparation

Create a folder, such as mine called extensions (the root directory mentioned later refers to this directory)
Create an img directory under the folder to store some pictures such as logo

Put in an image likelogo.png

Create an html directory under the folder to store html files
Create a js directory under the folder to store js files

Here, if you want to put a jqueryfile for loading first, it is also possible. I will scriptimport it later for convenience.

Create a css directory under the folder to store css files

Create a manifest.json file in the root directory of the folder

{
    
    
    "manifest_version":3,
	"name":"这是插件名称",
	"version":"0.0.1",
	"description":"这是插件描述",
    "action":{
    
    
		"default_title":"这是鼠标移上去时提示文字",
        "default_popup":"html/popup.html"
	},
	"icons":{
    
    
		"16":"img/logo.png",
		"32":"img/logo.png",
		"48":"img/logo.png",
		"128":"img/logo.png"
	}
}

Then the chrome extension [load the unzipped extension] select extensionsthe directory you just created

The effect is as follows

pupup part

  1. Create /htmla new popup.htmlfile, and then configure the path manifest.jsoninactionpopup

    "action":{
          
          
        "default_title":"这是鼠标移上去时提示文字",
        "default_popup":"html/popup.html"
    }
    
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <link rel="stylesheet" type="text/css" href="../css/popup.css" />
    </head>
    <body>
        <div class="btn">
            测试<input id="TEST" class="checkbtn" type="checkbox" />
        </div>
    </body>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="../js/popup.js"></script>
    </html>
    
  2. popup.cssCreate new files and popup.jsfiles in the css and js directories respectively

    /* popup.css */
    .btn{
          
          
        width: 100px;
        height: 30px;
        font-size: large;
    }
    
    //popup.js
    $(".checkbtn").click(function(){
          
          
        alert($(this).attr('id'));
    });
    
  3. then reload the extension

    Click on the plug-in, the effect is as follows

  4. outstanding issues

    The checked checkbox will be restored every time, so you need to make a local storage to save the popup changes

background part

  1. manifest.jsonThe config path added in service_workerandpermissions

    "background":{
          
          
        "service_worker":"background.js"
    },
    "permissions":["storage"]
    

    Note: service_workerinstructions

    1. This is a background script that always runs with the plugin
    2. There is no front-end page and dom is not supported, so jQuery and other js cannot be introduced
    3. All scripts that need to keep running need to be uninstalled directly background.jsin the file
    4. It is also not supported XMLHttpRequest, so it needs to be used fetchinstead of xhr request
    5. Be sure to put it in (the extension file root directory, not the computer disk root directory), otherwise a prompt 根目录will appear when usingservice worker(无效)
    6. You can view the output in 扩展程序=> 查看视图click the pop-up console
  2. background.jswrite files in the root directory

    //background.js
    chrome.runtime.onInstalled.addListener(() => {
          
          
        DBdata("clear");//清除插件保存的本地数据
    });
    //插件用的数据都存储在storage.local中
    function DBdata(mode,callback,data){
          
          //操作本地存储的函数
        if(mode=="set"){
          
          //保存本地数据
            console.log('set-LocalDB');
            chrome.storage.local.set({
          
          LocalDB: data});
        }else if(mode=="get"){
          
          //获取
            chrome.storage.local.get('LocalDB', function(response) {
          
          
                typeof callback == 'function' ? callback(response) : null;
            });
        }else if(mode=="clear"){
          
          //清空
            chrome.storage.local.clear();
        }
    }
    
  3. Open popup.js, delete the original click event, and add scripts to initialize and connect to service_worker

    //popup.js
    window.bgCommunicationPort = chrome.runtime.connect();//初始化bgCommunicationPort
    $(".checkbtn").click(function(){
          
          
        bgCommunicationPort.postMessage({
          
          //发送到bg,键值可以自由设置
            Direct : $(this).attr('id'),//目标
            Content : '测试内容',//内容
            step : 0//步骤
        });
    });
    $(document).ready(function(){
          
          //打开popup时触发,读取之前存储的参数
        bgCommunicationPort.postMessage({
          
          fromPopup:'getDB'});//向background发送消息
        bgCommunicationPort.onMessage.addListener(function(receivedPortMsg) {
          
          //监听background
            console.log(receivedPortMsg);//这是background发来的内容
            if(receivedPortMsg&&receivedPortMsg.Direct){
          
          
                $(".checkbtn").prop({
          
          'checked': false});//初始化按钮
                $("#"+receivedPortMsg.Direct).prop({
          
          'checked': true});
            }
        });
    });
    
  4. Open it background.jsand add a script to monitor popup (don’t delete the original one here, just add it later)

    //background.js
    chrome.runtime.onConnect.addListener(function(port) {
          
          //接收到popup
        port.onMessage.addListener(function(receivedMsg) {
          
          //监听popup发来的内容receivedMsg
            if(receivedMsg.fromPopup&&receivedMsg.fromPopup=='getDB'){
          
          //如果接收到了getDB,这里读取数据并返回相当于初始化popup页面
                DBdata('get',function(res){
          
          
                    port.postMessage(res.LocalDB);//发送到popup
                });
            }else{
          
          //如果不是,则说明是收到来自popup手动点击设置的数据,存入以用于popup打开时展示
                DBdata('set','',receivedMsg)
            }
        })
    });
    
  5. reload plugin

    1. At this time, you will find two errors

      It was found that the scriptimported jqueryreport cross-domain

      Then change to file import

      1. Create a new one in the js directoryjquery.js

        Go to https://jquery.com/download/ to download productionversion js

        and put his content jquery.jsin

      2. Modify popup.htmlthe jquery import in the file

        <script src="../js/jquery.js"></script>
        
    2. Reload the plug-in and find that the error is fine

  6. test

    The problem of resetting the check every time is already fixed

content part

content can be injected into the browsed webpage and manipulate the dom, so many functions can be realized

  1. manifest.jsoncontentThe configuration added in
"content_scripts":[{
    
    
    "js":["js/jquery.js","js/content.js"],/*content可以随意引入js,因为其内容会在浏览的网页上直接运行*/
    "matches":["*://localhost/*"],/*在哪些网页上运行*/
    "run_at":"document_end"/* 在页面加载完成时运行 */
}]

localhostNote that this is only matching now

  1. new js/content.js, content.jswrite in

    //content.js   manifest匹配地址的页面在刷新时会直接执行这里的代码
    chrome.runtime.sendMessage(chrome.runtime.id, {
          
          //当页面刷新时发送到bg
        fromContent: 'getDB'
    });
    
    chrome.runtime.onMessage.addListener(function(senderRequest, sender, sendResponse) {
          
          //接收到bg
        console.log('demo已运行');
        var LocalDB=senderRequest.LocalDB;
        console.log(LocalDB);
        switch(LocalDB.Direct){
          
          
            case 'TEST':
                console.log(123123);
                break;
            default:
                break;
        }
        // 不写会报错 Unchecked runtime.lastError: The message port closed before a response was received.
        sendResponse('这里是content返回值');
    });
    
  2. Then background.jsadd the monitoring contentcode in

    //background.js
    chrome.runtime.onMessage.addListener(function (senderRequest, sender, sendResponse) {
          
          //接收到content
        // 不写会报错 Unchecked runtime.lastError: The message port closed before a response was received.
        sendResponse({
          
           msg: '接收到content' });
        console.log(senderRequest);
        if (senderRequest.fromContent && senderRequest.fromContent == 'getDB') {
          
          //接收到fromContent:getDB
            DBdata('get', function (res) {
          
          //从本地取数据
                if (res.LocalDB) {
          
          
                    var LocalDB = res.LocalDB;
                    switch (LocalDB.Direct) {
          
          
                        //如果是存入的TEST按钮
                        case 'TEST':
                            chrome.tabs.query({
          
          
                                active: true,
                                currentWindow: true
                            }, function (tabs) {
          
          
                                chrome.tabs.sendMessage(tabs[0].id, {
          
           LocalDB: LocalDB }, function (res) {
          
          
                                    console.log('接收content的回调', res);
                                });//发送到content		
                            });
                            break;
                        default:
                            break;
                    }
                }
            });
        }
    });
    

    Pay attention to this method here sendResponse, some do not add this parameter in the callback, resulting in not being able to find

  3. reload plugin

  4. code execution order

    1. Plugin initialization phase

      1. First execute the plug-in initialization monitoring background.jsand onInstalledclear the local data
    2. Manually click the plug-in icon and check the plug-in

      1. Execute method popue.jsto readyinitialize
      2. Click the button to trigger clickthe method to modify the local data
    3. page refresh phase

      1. method content.jsof executionsendMessage
      2. method background.jsof executiononMessage
      3. read local data
      4. sendMessageDetermine whether to arrive based on local datacontent

Go to adware

Explanation: The judgment for the advertisement here is 类名为 .ad the element

Such as my vue page

<div>
    <h2 class="ad">第一条广告</h2>
    <h2 class="ad">第二条广告</h2>
    <h2 class="ad">第三条广告</h2>
    <h2>这里是正常的数据</h2>
</div>
  1. popup.htmladd one in去广告按钮

    <div class="btn">
        去广告<input id="removeAD" class="checkbtn" type="checkbox" />
    </div>
    
  2. In background.jsthe monitoring content part, increase removeADthe judgment of

    //如果是存入的removeAD按钮
    case 'removeAD':
        chrome.tabs.query({
          
          active: true, currentWindow: true
        }, function(tabs){
          
          
            chrome.tabs.sendMessage(tabs[0].id, {
          
          LocalDB: LocalDB});//发送到content		
        });
        break;
    
  3. The judgment added in content.jsthe monitoring background.jspartremoveAD

    case 'removeAD':
        //隐藏含有ad的元素,来达到去广告的效果
        $(".ad").hide();
        break;
    
  4. Reload the plug-in, check the page 去广告, and then refresh the page, and found that the advertisement is gone

Page redirection and cookies

And popupthe same, contentthe data will not be saved after closing. When we want to put the data on the B page after getting the data on the A page, the contentobtained data will not be passed in the direct jump, so it is backgroundeasy to save the local data with the link. came in handy

UserNickCase: Display the cookie of csdn localhost:8081in

  1. manifest.jsonConfigure [domain name script matching], [authority] and [host authority] in

    "permissions":["storage", "cookies"],
    "host_permissions": [
    	"*://www.csdn.net/*"
    ],
    "content_scripts":[{
          
          
    	"js":["js/jquery.js","js/content.js"],
    	"matches":["*://localhost/*", "*://www.csdn.net/*"],
    	"run_at":"document_end"
    }]
    

    What must be paid attention to here is that it must be able to match the url, otherwise it may cause 页面脚本无反应or获取不到cookie

  2. popup.htmladd button in

    <div class="btn">
        csdn<input id="checkCsdnUserNick" class="checkbtn" type="checkbox" />
    </div>
    
  3. background.jsAdd csdn按钮judgment for

    case 'checkCsdnUserNick':
        console.log('LocalDB', LocalDB)
        //popup设置数据的时候有个step属性,在多步操作的时候就开始发挥作用了
        if(LocalDB.step==0){
          
          
            LocalDB.step = 1;//将step设置成1
            chrome.storage.local.set({
          
          
                LocalDB: LocalDB//保存到本地数据
            },function() {
          
          
                chrome.tabs.update(null, {
          
          //将前台页面跳转到设置的url
                    // 这里的url不用带斜杠 /
                    url: 'https://www.csdn.net'
                });
            });
        }else if(LocalDB.step==1){
          
          //因为csdn的地址我们也匹配了所以content在跳转到csdn之后会还是会回来,不同的是step已经是1了
            chrome.cookies.get({
          
          //获取cookie
                'url': "https://www.csdn.net",
                'name': 'UserNick'
            }, function(cookie) {
          
          
                console.log('cookie', cookie);
                console.log(cookie.value);//获取到的值
                LocalDB.cookie=cookie.value;//把获取到的值放到本地数据的cookie属性里
                LocalDB.step = 2;//将step设置成2
                chrome.storage.local.set({
          
          //获取到cookie之后跳转到第二个页面
                    LocalDB: LocalDB//保存到本地数据
                },function() {
          
          
                    chrome.tabs.update(null, {
          
          //将前台页面跳转到设置的url
                        url: 'http://localhost:8081/'
                    });
                });
            });
        }else if(LocalDB.step==2){
          
          //第二步
            chrome.tabs.query({
          
          active: true, currentWindow: true}, function(tabs){
          
          //发送到content
                chrome.tabs.sendMessage(tabs[0].id, {
          
          LocalDB: LocalDB});		
            });
        }
        break;
    
  4. content.jsAdd csdn按钮judgment for

    case 'checkCsdnUserNick':
        if(LocalDB.step==2){
          
          
            $("body").append('<h1>'+LocalDB.cookie+'</h1>');
        }
        break;
    
  5. Reload the plug-in, csdn.netopen the plug-in in the middle, check it csdn, and then refresh the page to see the effect

    It will get the nickname in csdn.netZhongzhong cookie, and then jump to it localhost:8081for display

Record of problems in the process

  1. Unchecked runtime.lastError: The message port closed before a response was received.

    This problem is usually caused by other plugins, pay attention to troubleshooting, find the affected plugins and disable them

    Most people are caused by [Xunlei] plug-in or [Oil Monkey] plug-in

  2. There is an additional [Error] button next to the extension [Remove]

    1. If there is an error message, just follow the prompts to troubleshoot

    2. If there is no error message, try to remove the extension and reload it

    3. Check whether it is not used together sendMessagewithsendResponse

      Each sendMessageneeds and sendResponseresponds

      That is to say, in each chrome.runtime.onMessage.addListenercallback function, you need to use sendResponseto return

      See https://blog.csdn.net/m0_37729058/article/details/89186257

  3. service worker can't content.jsseeconsole.log

    The reason is because this js is embedded in the page, so it needs to be viewed in the console of the web page used instead ofservice worker

  4. Can't get cookie

    The possible reasons are as follows

    1. Without authorization host_permissions, the console will report an errorUnchecked runtime.lastError: No host permissions for cookies at url: "https://www.csdn.net/".

      Check manifest.jsonthis configuration, if it is not added or not matched, it will cause this problem

    2. Check chrome.cookies.getthat this method urlis completely correct

code address

github jump

Guess you like

Origin blog.csdn.net/qq_43382853/article/details/126272794