jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

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}

  • tabs controls and observes tabs; reading URLs needs permission {tabs điều khiển và quan sát tab; đọc URL cần quyền}.
  • scripting injects JS/CSS on demand — pair with activeTab {scripting tiêm JS/CSS theo yêu cầu — ghép với activeTab}; pass data via args.
  • contextMenus must be created in onInstalled {contextMenus phải tạo trong onInstalled}.
  • commands add rebindable shortcuts; _execute_action opens the popup {commands thêm phím tắt gán lại được; _execute_action mở popup}.
  • notifications reach the user anytime — use sparingly {notifications tớ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}.