Вы когда-нибудь хотели создать собственное расширение для Chrome, но не знали, с чего начать? Ты не один! В этом руководстве мы рассмотрим шаги по разработке простого расширения Chrome под названием «Yeti Boss». Даже если вы новичок в разработке расширений, не волнуйтесь — это не будет сложно!

Йети Босс?

Я просто дам вам небольшую предысторию. Yeti Boss — это дань уважения старой игре Yeti Sports, которая была популярна в офисах несколько лет назад. В игре был скрытый режим босса, который можно было активировать, нажав «b» (возможно, я играл в него некоторое время назад). Когда был активирован Режим босса, на экране отображалось наложение с несколькими случайными диаграммами, чтобы создать впечатление, что игра была чем-то серьезным. Мы создадим расширение, которое имитирует этот эффект.

Идея

Расширение будет иметь состояние ON/OFF, которое можно вызвать с помощью сочетания клавиш ⌘ + Shift + Y (на Mac) или Ctrl + Shift + Y (на Windows). Когда расширение включено, оно будет внедрять CSS и HTML на веб-страницу, чтобы скрыть фактический контент. Когда он выключен, он очистит HTML и удалит внедрённый CSS.

Структура папок

Вот структура папок, которую мы будем использовать для этого расширения:

base/
├── scripts/
│   ├── background.js
│   ├── boss.js
├── styles/
│   ├── injected.css
├── images/
│   ├── chart.png
│   ├── code.png
├── icons/
│   ├── icon.png
│   ├── icon-16.png
│   ├── icon-32.png
│   ├── icon-64.png
│   ├── icon-128.png
├── manifest.json

Манифест

Файл «manifest.json» — единственный необходимый файл для расширения Chrome, и он содержит необходимую информацию о расширении. В этом примере мы видим, что он определяет версию манифеста, имя расширения, описание и номер версии. Кроме того, он включает информацию о действии расширения, командах, значках, фоне, разрешениях, сценариях содержимого и ресурсах, доступных в Интернете.

  • В разделе «commands» манифеста задается сочетание клавиш для включения расширения.
  • Раздел background определяет сценарий сервисного работника, который будет запускаться при загрузке расширения.
  • Раздел «разрешения» запрашивает разрешение на доступ к активной вкладке и выполнение некоторых скриптов.
  • Раздел «content_scripts» внедряет скрипт под названием «boss.js».
  • Раздел «web_accessible_resources» делает доступной папку с изображениями.
{
  "manifest_version": 3,
  "name": "Yeti Boss",
  "description": "Boilerplate",
  "version": "1.0",
  "action": {
    "default_icon": {
      "16": "icons/icon-16.png",
      "32": "icons/icon-32.png",
      "48": "icons/icon-48.png",
      "128": "icons/icon-128.png"
    }
  },
  "commands": {
    "_execute_action": {
      "suggested_key": {
        "default": "Ctrl+Y",
        "mac": "Command+Y"
      }
    }
  },
  "icons": {
    "16": "icons/icon-16.png",
    "32": "icons/icon-32.png",
    "48": "icons/icon-48.png",
    "128": "icons/icon-128.png"
  },
  "background": {
    "service_worker": "scripts/background.js"
  },
  "permissions": ["activeTab", "scripting"],
  "content_scripts": [
    {
      "js": ["scripts/boss.js"],
      "matches": ["<all_urls>"]
    }
  ],
  "web_accessible_resources": [{
    "resources": [ "images/*", "/images/*" ],
    "matches": ["<all_urls>"]
    }]
}

Сервисный работник

Service Worker отвечает за настройку событий и запуск действий в ответ на ввод данных пользователем. В этом примере сервисный работник прослушивает щелчок по значку расширения и получает текущее состояние значка. Состояние значка переключается на противоположное его текущему состоянию, и значок обновляется соответствующим образом. Действия (bossIsComing и bossIsAway) определены в файле «boss.js».

// scripts/background.js

// Set the badge and the initial action state: "off"
chrome.runtime.onInstalled.addListener(() => {
  chrome.action.setBadgeText({ text: "off" });
  chrome.action.setBadgeTextColor({ color: "#333" });
  chrome.action.setBadgeBackgroundColor({ color: "#f50057" });
});


// Listener for click on the action Icon
chrome.action.onClicked.addListener(async (tab) => {
  // Retrieve the action badge to check if the extension is 'on' or 'off'
  const prevState = await chrome.action.getBadgeText({ tabId: tab.id });
  // Next state will always be the opposite
  const nextState = prevState === 'on' ? 'off' : 'on'

  // Set the action badge to the next state
  await chrome.action.setBadgeText({
    tabId: tab.id,
    text: nextState,
  });

  if (nextState === "on") {
    // Insert the CSS file when the user turns the extension on
    await chrome.scripting.insertCSS({
      files: ["styles/injected.css"],
      target: { tabId: tab.id },
    });
    // Run bossIsComing
    chrome.scripting.executeScript(
      {func : ()=>bossIsComing(), target: { tabId: tab.id }}
    )
  } else if (nextState === "off") {
    // Run bossIsAway
    chrome.scripting.executeScript(
      {func : ()=>bossIsAway(), target: { tabId: tab.id }}
    )

    // Remove the CSS file when the user turns the extension off
    await chrome.scripting.removeCSS({
      files: ["styles/injected.css"],
      target: { tabId: tab.id },
    });
  }
});

Действия:

Когда расширение включено, сервис-воркер вставляет файл CSS во вкладку и выполняет функцию «bossIsComing». Функция создает элемент div со случайным заголовком и изображением из предопределенных массивов и добавляет его в тело страницы.

Когда расширение отключено, сервисный работник выполняет функцию «bossIsAway». Функция удаляет элемент div со страницы, если он существует.

// scripts/boss.js

// Create and append HTML to the body
window.bossIsComing = function(){
  let div = document.createElement('div');
  div.className = 'yetiBoss';

  let h1 = document.createElement('h1');
  let titles = ['Increase Node.js performance', 'NPM: UI library comparisons', 'Upgrading React version and it\'s dependencies', 'Next.JS Boilerplate']
  h1.textContent = titles[Math.floor(Math.random() * titles.length)];

  let img = document.createElement('img');
  let srcs = ['images/code.png', 'images/chart.png'];
  img.src = chrome.runtime.getURL(srcs[Math.floor(Math.random() * srcs.length)]);
  
  div.appendChild(h1);
  div.appendChild(img);

  document.body.appendChild(div)
}

// Remove HTML if extists
window.bossIsAway = function(){
  let div = document.querySelector('.yetiBoss');
  div !== null && document.body.removeChild(div)
}

Стиль

Стиль CSS в нашем расширении может показаться немного длинным, но на самом деле он довольно прост. Начальный раздел стиля служит для сброса всех существующих стилей на веб-странице, обеспечивая чистоту нашего расширения. Этот шаг очень важен, так как на странице уже могут быть определены стили, которые могут мешать нашим собственным.

После того, как мы сбросим стили, мы можем легко расположить наш контент, чтобы он зафиксировался на месте и занял весь экран. Чтобы наш контент отображался выше любых других элементов на странице, мы устанавливаем высокое значение z-index. Мы также определяем стили для заголовка и изображения, чтобы они отображались должным образом.

/* http://meyerweb.com/eric/tools/css/reset/
   v2.0 | 20110126
   License: none (public domain)
*/

html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
  display: block;
}
body {
  line-height: 1;
}
ol,
ul {
  list-style: none;
}
blockquote,
q {
  quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
  content: "";
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

/* Boss Mode Style*/

.yetiBoss{
  position: fixed;
  z-index: 9999;
  width: 100vw;
  height: 100vh;
  top:0;
  left: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #1e1e1e;
  padding: 24px;
  box-sizing: border-box;
}

.yetiBoss h1{
  color: #F8F8F8;
  margin: 12px;
}

.yetiBoss img{
  margin: 12px;
  width: 1024px;
  height: auto;
}

Изображения

Чтобы добавить изображения в расширение, просто скопируйте файлы изображений, которые вы хотите использовать, в папку «images». В демо-коде я использовал поддельную диаграмму и снимок кода, но вы можете использовать любые изображения, которые вам нравятся. Просто не забудьте обновить имена файлов в переменной «srcs» в файле «scripts/boss.js», чтобы они соответствовали именам ваших файлов изображений.

Иконки

Иконки, используемые в расширении, представляют собой набор файлов PNG разных размеров. Вы можете заменить их своими значками, если хотите.

Как загрузить расширение

Чтобы загрузить расширение в свой браузер, выполните следующие простые действия.

  1. Откройте браузер и перейдите к «chrome://extensions/».
  2. В правом верхнем углу страницы включите «Режим разработчика».
  3. Нажмите на кнопку «Загрузить распакованное» и выберите папку, содержащую ваше расширение.
  4. После загрузки расширения вы должны увидеть его в списке установленных расширений.
  5. Перейдите на веб-сайт и используйте сочетание клавиш ⌘ + Shift + Y (на Mac) или Ctrl + Shift + Y (на Windows).

сделай сам

Если вы заинтересованы в создании собственной версии этого расширения, вы можете скачать zip или клонировать репозиторий с GitHub. Когда у вас есть файлы на вашем локальном компьютере, вы можете изменить код в соответствии со своими потребностями. Обязательно обращайте внимание на имена файлов, пути и другие зависимости при внесении изменений. Вы также можете настроить изображения и значки, используемые в расширении, заменив их своими собственными. Получайте удовольствие, экспериментируя с разными стилями и функциями!



Если у вас есть какие-либо предложения для будущих статей по этой теме, не стесняйтесь поделиться ими в комментариях ниже.