jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Build Chrome Extensions · Part 2 — The Manifest, Deep Dive

Every manifest.json key that matters: action, permissions vs host_permissions, content_scripts, background, icons, web_accessible_resources, commands and side_panel — with an interactive manifest builder.

The manifest.json is the contract between your extension and the browser {manifest.json là hợp đồng giữa extension và trình duyệt}. Get it right and everything else falls into place; get it wrong and nothing loads {Làm đúng thì mọi thứ vào khớp; làm sai thì không gì nạp được}. This part is the reference you’ll return to constantly {Phần này là tài liệu tham chiếu bạn sẽ quay lại liên tục}.

Toggle features in the builder and watch the manifest assemble — refer to it as we go {Bật tính năng trong builder và xem manifest tự ráp — tham chiếu khi ta đi}:


1. The identity keys {Các key định danh}

{
  "manifest_version": 3,
  "name": "My Extension",
  "version": "1.0.0",
  "description": "Short description shown in the store and management page.",
  "icons": { "16": "icons/16.png", "48": "icons/48.png", "128": "icons/128.png" }
}
  • manifest_version: must be 3 {phải là 3}.
  • version: a dotted number string ("1.0.3"); the Web Store requires you to bump it on every upload {chuỗi số có dấu chấm; Web Store yêu cầu tăng mỗi lần upload}.
  • icons: provide 16 (favicon-size), 48 (management page), 128 (store & install) at minimum {cung cấp tối thiểu 16, 48, 128}.

2. action — the toolbar button {action — nút trên thanh công cụ}

"action": {
  "default_popup": "popup.html",
  "default_title": "My Extension",
  "default_icon": { "16": "icons/16.png", "48": "icons/48.png" }
}

If you omit default_popup, clicking the icon fires chrome.action.onClicked instead — useful for toggle-style extensions with no UI {Nếu bỏ default_popup, bấm icon sẽ kích hoạt chrome.action.onClicked — hữu ích cho extension kiểu bật/tắt không có UI}.


3. permissions vs host_permissions {permissions vs host_permissions}

This distinction is the single most important thing in the manifest {Sự phân biệt này là điều quan trọng nhất trong manifest}.

  • permissions — access to chrome.* APIs (capabilities) {quyền truy cập API chrome.* (khả năng)}:
"permissions": ["storage", "tabs", "scripting", "contextMenus", "notifications", "alarms"]
  • host_permissions — access to specific websites’ data (origins) {quyền truy cập dữ liệu của website cụ thể (origin)}:
"host_permissions": ["https://*.github.com/*", "https://api.example.com/*"]

The mental split {Phân chia tư duy}: permissions says “I want to use this browser feature”; host_permissions says “I want to read/modify these sites” {permissions nói “tôi muốn dùng tính năng trình duyệt này”; host_permissions nói “tôi muốn đọc/sửa các site này”}.

activeTab is your friend. Instead of broad host_permissions, the activeTab permission grants temporary access to the current tab only when the user clicks your icon — far less scary at install and faster store review {activeTab là bạn của bạn. Thay vì host_permissions rộng, quyền activeTab cấp truy cập tạm thời vào tab hiện tại chỉ khi user bấm icon — đỡ đáng sợ lúc cài và review store nhanh hơn}.

We dedicate all of Part 9 to using these safely {Ta dành cả Phần 9 để dùng chúng an toàn}.


4. content_scripts — auto-injected page code {content_scripts — code tự tiêm vào trang}

"content_scripts": [
  {
    "matches": ["https://*.github.com/*"],
    "js": ["content.js"],
    "css": ["content.css"],
    "run_at": "document_idle"
  }
]
  • matches: match patterns deciding which pages get the script ("<all_urls>", "https://*/*", specific origins) {mẫu khớp quyết định trang nào được tiêm script}.
  • run_at: document_start (before DOM), document_end (DOM ready), or document_idle (default, after load) {thời điểm chạy}.
  • You can also exclude_matches, target all_frames, and match by glob {cũng có exclude_matches, all_frames, và khớp theo glob}.

Full treatment in Part 4 {Đầy đủ ở Phần 4}.


5. background — the service worker {background — service worker}

"background": { "service_worker": "background.js", "type": "module" }

In MV3 the background is a service worker, not a persistent page {Ở MV3 nền là một service worker, không phải trang thường trú}. "type": "module" lets you use import in it {"type": "module" cho phép dùng import}. Part 5 covers its event-driven lifecycle {Phần 5 nói về vòng đời theo sự kiện của nó}.


6. UI surfaces: options_page & side_panel {Bề mặt UI: options_page & side_panel}

"options_page": "options.html",
"side_panel": { "default_path": "panel.html" }

options_page is a full settings page; side_panel (Chrome 114+) docks a persistent panel beside the page (needs the "sidePanel" permission) {options_page là trang cài đặt đầy đủ; side_panel (Chrome 114+) gắn một panel thường trú cạnh trang (cần quyền "sidePanel")}. Both covered in Part 8 {Cả hai ở Phần 8}.


7. commands — keyboard shortcuts {commands — phím tắt}

"commands": {
  "toggle-feature": {
    "suggested_key": { "default": "Ctrl+Shift+Y", "mac": "Command+Shift+Y" },
    "description": "Toggle the feature"
  },
  "_execute_action": { "suggested_key": { "default": "Ctrl+Shift+E" } }
}

The special _execute_action opens your popup via keyboard {_execute_action đặc biệt mở popup bằng bàn phím}. Custom commands fire chrome.commands.onCommand in the service worker {Lệnh tùy biến kích hoạt chrome.commands.onCommand trong service worker}.


8. web_accessible_resources — exposing files to pages {web_accessible_resources — phơi file cho trang}

By default a web page can’t load files from your extension {Mặc định trang web không nạp được file từ extension của bạn}. To inject an image or a script into the page, you must whitelist it {Để tiêm một ảnh hoặc script vào trang, bạn phải whitelist}:

"web_accessible_resources": [
  { "resources": ["inject.js", "logo.png"], "matches": ["https://*/*"] }
]

MV3 requires the matches scoping (you can’t expose to everyone blindly) — this closes a real security hole from MV2 {MV3 yêu cầu giới hạn matches (không phơi cho tất cả một cách mù quáng) — vá một lỗ bảo mật thật từ MV2}.


9. The full picture {Bức tranh đầy đủ}

A realistic manifest for a moderately powerful extension {Một manifest thực tế cho extension tương đối mạnh}:

{
  "manifest_version": 3,
  "name": "GitHub Helper",
  "version": "1.2.0",
  "description": "Adds shortcuts and metrics to GitHub.",
  "icons": { "16": "icons/16.png", "48": "icons/48.png", "128": "icons/128.png" },
  "action": { "default_popup": "popup.html" },
  "background": { "service_worker": "background.js", "type": "module" },
  "content_scripts": [
    { "matches": ["https://github.com/*"], "js": ["content.js"], "run_at": "document_idle" }
  ],
  "permissions": ["storage", "activeTab", "scripting", "contextMenus"],
  "host_permissions": ["https://api.github.com/*"],
  "options_page": "options.html",
  "commands": { "_execute_action": { "suggested_key": { "default": "Ctrl+Shift+G" } } }
}

10. Exercises {Bài tập}

1. Which key would you add to access chrome.storage, and which to read pages on https://news.ycombinator.com? {Key nào để truy cập chrome.storage, key nào để đọc trang https://news.ycombinator.com?}

Solution {Lời giải}

"permissions": ["storage"] for the API; "host_permissions": ["https://news.ycombinator.com/*"] for the site {permissions cho API; host_permissions cho site}.

2. You want a hotkey that opens your popup. What do you add? {Bạn muốn một phím tắt mở popup. Thêm gì?}

Solution {Lời giải}
"commands": { "_execute_action": { "suggested_key": { "default": "Ctrl+Shift+E" } } }

3. Why prefer activeTab over "host_permissions": ["<all_urls>"] when possible? {Vì sao nên ưu tiên activeTab hơn "host_permissions": ["<all_urls>"] khi có thể?}

Solution {Lời giải}

activeTab grants temporary, click-triggered access to just the current tab — least privilege, less scary install prompt, easier store review {activeTab cấp truy cập tạm thời, kích hoạt bằng click, chỉ tab hiện tại — đặc quyền tối thiểu, prompt cài đỡ đáng sợ, review store dễ hơn}.

Stretch {Nâng cao}: in the builder, enable content scripts + host permissions + a command, and read off which permissions array entries appear {trong builder, bật content scripts + host permissions + một command, và đọc xem entry nào xuất hiện trong mảng permissions}.


Key takeaways {Điểm chính}

  • The manifest is the contract; only manifest_version/name/version are required, the rest declare capabilities {Manifest là hợp đồng; chỉ ba key bắt buộc, còn lại khai báo khả năng}.
  • permissions = chrome.* APIs; host_permissions = site access — keep both minimal {permissions = API chrome.*; host_permissions = truy cập site — giữ cả hai tối thiểu}.
  • Each major surface has a manifest key: action, content_scripts, background, options_page, side_panel, commands {Mỗi bề mặt lớn có một key manifest}.
  • web_accessible_resources (with matches) is required to expose extension files to web pages {web_accessible_resources (kèm matches) cần để phơi file extension cho trang}.

Next up {Tiếp theo}

Part 3 — Architecture & the component model: how the popup, service worker, content scripts, and options page relate, what each can and cannot do, and the data-flow picture that makes the rest of the series click {Phần 3 — Kiến trúc & mô hình component: popup, service worker, content script, options page liên hệ ra sao, mỗi cái làm đượckhông làm được gì, và bức tranh luồng dữ liệu giúp phần còn lại của series “thông”}.