Build Chrome Extensions · Part 10 — The Powerful APIs
tabs, scripting, contextMenus, commands (keyboard shortcuts) and notifications — the toolkit that turns a toy into a real product. With an interactive API explorer.
You now understand the architecture; this part is about reach {Bạn đã hiểu kiến trúc; phần này về tầm với}. These five APIs are what let an extension control tabs, inject behavior on demand, add menu items, bind shortcuts, and notify the user {Năm API này là thứ cho extension điều khiển tab, tiêm hành vi theo yêu cầu, thêm mục menu, gán phím tắt, và báo cho user}. Master them and you can build almost anything {Thạo chúng thì bạn xây được gần như mọi thứ}.
Switch tabs and “run” each API below {Chuyển tab và “chạy” từng API bên dưới}:
1. chrome.tabs — control the browser {chrome.tabs — điều khiển trình duyệt}
Query, create, update, reload, group, and move tabs {Truy vấn, tạo, cập nhật, tải lại, nhóm, và di chuyển tab}:
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.create({ url: "https://example.com" });
chrome.tabs.update(tab.id, { url: "https://other.com" });
chrome.tabs.onUpdated.addListener((id, info) => {
if (info.status === "complete") onPageReady(id);
});
Reading tab url/title requires the "tabs" permission or a matching host permission; merely creating or updating tabs does not {Đọc url/title của tab cần quyền "tabs" hoặc host permission khớp; chỉ tạo/cập nhật tab thì không}. onUpdated is your hook for “the user navigated” {onUpdated là móc cho “user đã điều hướng”}.
2. chrome.scripting — inject on demand {chrome.scripting — tiêm theo yêu cầu}
The MV3 way to run code in a page from the worker — replaces V2’s tabs.executeScript {Cách MV3 chạy code trong trang từ worker — thay cho tabs.executeScript của V2}:
// run an inline function
await chrome.scripting.executeScript({
target: { tabId },
func: (color) => { document.body.style.background = color; },
args: ["#c8ff00"], // arguments are serialized to the page
});
// or inject files
await chrome.scripting.executeScript({ target: { tabId }, files: ["content.js"] });
await chrome.scripting.insertCSS({ target: { tabId }, files: ["overlay.css"] });
Pair it with activeTab for click-driven tools that need no host warning (Part 9) {Ghép với activeTab cho công cụ điều khiển bằng click mà không cần cảnh báo host (Phần 9)}. The func runs in the page’s isolated world, so it can’t close over your worker’s variables — pass data via args {func chạy trong thế giới cô lập của trang, nên không bắt được biến của worker — truyền dữ liệu qua args}.
3. chrome.contextMenus — right-click actions {chrome.contextMenus — hành động chuột phải}
Add items to the browser’s right-click menu, scoped to a context {Thêm mục vào menu chuột phải của trình duyệt, giới hạn theo ngữ cảnh}:
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "define",
title: 'Define "%s"', // %s = the selected text
contexts: ["selection"], // also: "page", "link", "image", "video"
});
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "define") lookup(info.selectionText);
});
Create menus in onInstalled (not on every wake, or you’ll get duplicate-id errors) {Tạo menu trong onInstalled (không phải mỗi lần thức, kẻo lỗi trùng id}). Try the right-click box in the explorer {Thử ô chuột phải trong explorer}.
4. chrome.commands — keyboard shortcuts {chrome.commands — phím tắt}
Power users love shortcuts {Power user yêu phím tắt}. Declare them in the manifest; Chrome lets users rebind them at chrome://extensions/shortcuts {Khai báo trong manifest; Chrome cho user gán lại tại chrome://extensions/shortcuts}:
{
"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" } }
}
}
chrome.commands.onCommand.addListener((command) => {
if (command === "toggle-feature") toggleFeature();
});
The special _execute_action command opens your popup {Lệnh đặc biệt _execute_action mở popup của bạn}. Press Shift+Y in the explorer’s commands tab to fire one {Bấm Shift+Y trong tab commands của explorer để kích hoạt}.
5. chrome.notifications — reach the user {chrome.notifications — tới user}
Rich OS-level notifications, even when no UI is open {Thông báo cấp OS phong phú, cả khi không có UI nào mở}:
chrome.notifications.create("sync-done", {
type: "basic",
iconUrl: "icon128.png",
title: "Sync complete",
message: "42 items updated.",
buttons: [{ title: "View" }],
});
chrome.notifications.onButtonClicked.addListener((id, index) => {
if (id === "sync-done" && index === 0) openResults();
});
Use sparingly — notification fatigue makes users uninstall {Dùng tiết kiệm — mệt mỏi thông báo khiến user gỡ cài}.
6. Putting it together {Ghép lại}
A realistic “highlighter” extension uses all of these {Một extension “tô sáng” thực tế dùng tất cả}: a command or action click triggers the worker, which uses scripting to inject into the activeTab, a contextMenu offers “highlight selection”, and a notification confirms when done — all reading settings from chrome.storage {một click command hoặc action kích worker, worker dùng scripting để tiêm vào activeTab, một contextMenu cung cấp “tô sáng vùng chọn”, và một notification xác nhận khi xong — tất cả đọc cài đặt từ chrome.storage}. That’s the whole series working as one system {Đó là cả series hoạt động như một hệ thống}.
7. Exercises {Bài tập}
1. You want to invert the current page’s colors when the user clicks your icon, with no install warning. Which APIs and permission? {Bạn muốn đảo màu trang hiện tại khi user bấm icon, không cảnh báo cài đặt. API và quyền nào?}
Solution {Lời giải}
activeTab + scripting; on action.onClicked, call chrome.scripting.executeScript with an inline func {activeTab + scripting; trong action.onClicked, gọi chrome.scripting.executeScript với func inline}.
2. Your contextMenus.create throws “duplicate id” sometimes. Why? {contextMenus.create đôi khi ném “duplicate id”. Vì sao?}
Solution {Lời giải}
You’re creating it on every worker wake. Create menus only in onInstalled {Bạn tạo nó mỗi lần worker thức. Chỉ tạo menu trong onInstalled}.
3. You pass a worker variable into executeScript’s func via closure and it’s undefined in the page. Fix it. {Bạn truyền biến của worker vào func của executeScript qua closure và nó undefined trong trang. Sửa đi.}
Solution {Lời giải}
The function is serialized and runs in the page’s world — it can’t close over worker variables. Pass them through the args array {Hàm được serialize và chạy trong thế giới của trang — không bắt được biến worker. Truyền chúng qua mảng args}.
Stretch {Nâng cao}: in the explorer’s scripting tab, inject the invert, then reset — that’s exactly the executeScript round-trip you’d ship {trong tab scripting của explorer, tiêm đảo màu, rồi reset — đó chính là vòng executeScript bạn sẽ ship}.
Key takeaways {Điểm chính}
tabscontrols and observes tabs; reading URLs needs permission {tabsđiều khiển và quan sát tab; đọc URL cần quyền}.scriptinginjects JS/CSS on demand — pair withactiveTab{scriptingtiêm JS/CSS theo yêu cầu — ghép vớiactiveTab}; pass data viaargs.contextMenusmust be created inonInstalled{contextMenusphải tạo trongonInstalled}.commandsadd rebindable shortcuts;_execute_actionopens the popup {commandsthêm phím tắt gán lại được;_execute_actionmở popup}.notificationsreach the user anytime — use sparingly {notificationstới user bất cứ lúc nào — dùng tiết kiệm}.
Next up {Tiếp theo}
Part 11 — Pro tooling & build: why plain files don’t scale, and how to set up Vite + CRXJS + TypeScript + React for HMR, bundling, and a typed, modern developer experience {Phần 11 — Công cụ & build chuyên nghiệp: vì sao file thuần không mở rộng được, và cách thiết lập Vite + CRXJS + TypeScript + React cho HMR, đóng gói, và trải nghiệm dev hiện đại, có kiểu}.