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 be3{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 tochrome.*APIs (capabilities) {quyền truy cập APIchrome.*(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”}.
activeTabis your friend. Instead of broadhost_permissions, theactiveTabpermission grants temporary access to the current tab only when the user clicks your icon — far less scary at install and faster store review {activeTablà bạn của bạn. Thay vìhost_permissionsrộng, quyềnactiveTabcấ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), ordocument_idle(default, after load) {thời điểm chạy}.- You can also
exclude_matches, targetall_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/versionare 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= APIchrome.*;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(withmatches) is required to expose extension files to web pages {web_accessible_resources(kèmmatches) 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 được và khô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”}.