快速制作自己的第一个Edge扩展(兼Chrome扩展)

Introduction

事情的起因是这样的,最近想给自己的Chrome浏览器添加一个功能,就是在自己离开电脑的时候能够暂时模糊浏览器标签页的内容,加密标题和标签页的logo,以防止他人趁机偷窥隐私。我在谷歌浏览器上搜了很多插件,比较相似的当属下面这个名为Hide!!!的插件,它的工作原理就是按住Alt+P就可以暂时关闭所有的页面,再次按下Alt+P就可以恢复之前打开的所有页面。但是感觉和自己的需求还是有所区别,于是打算自己动手做一下


Body

No.1 搭建浏览器扩展的开发环境(以vscode为例)

按下Ctrl+Shift+X打开扩展侧边栏,输入Chrome,选择如下图所示的第一个安装

安装完成后按住Ctrl+Shift+P打开命令面板,输入Chrome,如下图所示:

正常情况下,会出现三个选项

复制代码
Chrome Extension:Create New

Chrome Extension:Build Files

Chrome Extension:Watch Files

点击Create New选项,会进入一下界面:

Extension Type有三种:

复制代码
Popup ; Override Page ; Dev tools
Popup(弹出页)
点击扩展图标时可以临时显示这个页面,生命周期短暂
Override Page(覆盖页)
可用于替换Chrome的默认界面
DevTools Page(开发者工具页)
用于扩展Chrome开发者工具的功能,它会在开发者工具打开时加载

目前的话选择Popup并创建项目

NO.2 编写代码

项目模板中关键的几个文件为background.js ,contentScript.js,popup.js,以及public文件夹里的popup.html,manifest.json.

background.js

javascript 复制代码
let isLocked = false;

chrome.runtime.onInstalled.addListener(()=>{
  chrome.storage.local.set({isLocked:false});
});

chrome.runtime.onStartup.addListener(()=>{
  chrome.storage.local.get([isLocked],(result)=>{
    isLocked = !!result.isLocked;
  })
});

// 监听HTML按钮
chrome.runtime.onMessage.addListener((message,send,sendResponse)=>{
  if(message.action==="toggle"){
    toggle();
    sendResponse({success:true});
  }
  else if(message.action==="getStatus")
    sendResponse({isLocked});
  return true;
});

//监听Alt+P

chrome.commands.onCommand.addListener((command)=>{
  if(command==='toggle')
    toggle();
});

async function toggle() {
  isLocked = !isLocked;
  chrome.storage.local.set({isLocked});

  const tabs = await chrome.tabs.query({url:["<all_urls>"]});

  for(const tab of tabs){
    if(tab.id){
      chrome.tabs.sendMessage(tab.id,{
        action:"update",
        isLocked
      }).catch(()=>{});
    }
  }
}

contentScript.js

javascript 复制代码
let originalTitle = document.title;
let originalFavicon =null;
let isPageLocked = false;

const faviconLink = document.querySelector("link[rel*='icon']");
if(faviconLink){
  originalFavicon = faviconLink.href;
}

chrome.runtime.sendMessage({action:"getStatus"},(response)=>{
  if(chrome.runtime.lastError)return;
  if(response && typeof response.isLocked === 'boolean'){
    isPageLocked = response.isLocked;
    encryptFavicon(isPageLocked);
    applyBlur(isPageLocked);
    encryptTitle(isPageLocked);
  }
})



chrome.runtime.onMessage.addListener((message) => {
  if (message.action === 'update') {
    isPageLocked = message.isLocked;
    encryptFavicon(isPageLocked);
    applyBlur(isPageLocked);
    encryptTitle(isPageLocked);
  }
});


function applyBlur(isLocked){
  const styleId = "blur-style";
  const existing = document.getElementById(styleId);
  if(isLocked){
    if(existing)return;
    const style = document.createElement("style");
    style.id = styleId;
    style.textContent = `
    html, body, body * {
        filter: blur(5px) !important;
        pointer-events: none !important;
        user-select: none !important;
      }
      body::before {
        content: '';
        position: fixed;
        top: 0; left: 0;
        width: 100%; height: 100%;
        z-index: 2147483647;
        pointer-events: all;
        cursor: not-allowed;
      }
    `;
    document.head.appendChild(style);
  }else{
    if(existing)
      existing.remove();
  }
}

function encryptFavicon(isLocked){
  let link = document.querySelector("link[rel*='icon']") || document.createElement('link');
  console.log(link);
  link.type = 'image/x-icon';
  link.rel = 'shortcut icon';
  link.href = isLocked?chrome.runtime.getURL('lock.png'):(originalFavicon || '');
  console.log(link);
  document.head.appendChild(link);
}



function encryptTitle(isLocked){
  document.title = isLocked?'*'.repeat(originalTitle.length):originalTitle;
}

popup.js

javascript 复制代码
document.addEventListener('DOMContentLoaded',()=>{
  const toggleBtn = document.getElementById("hider-btn");
  const display = document.getElementById("hider-show");

  chrome.storage.local.get(['isLocked'],(result)=>{
    updateStatus(result.isLocked);
  })

  toggleBtn.addEventListener('click', () => {
    chrome.runtime.sendMessage({ action: "toggle" }, () => {
      chrome.storage.local.get(['isLocked'], (result) => {
        updateStatus(result.isLocked);
      });
    });
  });

  function updateStatus(isBlurred) {
    display.textContent = `Status: ${isBlurred ? 'Locked' : 'Unlocked'}`;
  }
});

popup.html

javascript 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Privacy Hider</title>
  </head>
  <body>
    <span>Current Status</span>
    <span id="hider-show"></span>
    <button id="hider-btn"></button>
    <script src="popup.js"></script>
    <style>
      body{
        width:250px;
        height: 300px;
        font-family: Arial, sans-serif;
      }
    </style>
  </body>
</html>

manifest.json

javascript 复制代码
{
  "manifest_version": 3,
  "name": "Privacy Hider",
  "$schema": "https://json.schemastore.org/chrome-manifest.json",
  "version": "0.2.0",
  "description": "Protect your privacy by blurring all the pages at the same time disabling interaction and encrypting the name of the tabs",
  "icons": {
    "16": "icons/icon_16.png",
    "32": "icons/icon_32.png",
    "48": "icons/icon_48.png",
    "128": "icons/icon_128.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_title": "Privacy Protector",
    "default_popup": "popup.html"
  },
  "permissions": [
    "storage",
    "tabs",
    "activeTab",
    "scripting"
  ],
  "commands": {
    "toggle":{
      "suggested_key": {
        "default":"Alt+P"
      },
      "description": "Toggle blur,encrypt titles and disable interaction."
    }
  },
  "content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "run_at": "document_idle",
      "js": [
        "contentScript.js"
      ]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["lock.png"],
      "matches": ["<all_urls>"]
    }
  ]
}

并且准备一个lock.png到public目录下马,用于锁定时的标签页logo

准备好上面的一切好,就可以在项目目录的终端输入

javascript 复制代码
npm run build

这将会在项目目录生成build文件夹,如何在浏览器中运行我们的扩展呢

打开谷歌浏览器的管理扩展程序页面

做两件事,首先打开右上角的开发者模式,其次就是点击左侧的"加载未打包的扩展程序",选择build文件夹,这样我们的扩展就顺利加入到google中

尝试使用一下:

Conclusion

该扩展仍然有很多可以改进的地方,并且在替换favicon上存在一些bug,以后可以加入密码验证功能。