第03章 Manifest 文件详解
12/3/25About 10 min
第 3 章:Manifest 文件详解
学习目标
- 深入理解 manifest.json 的各个配置项
- 掌握权限声明和安全配置
- 了解 Manifest V2 和 V3 的差异
知识点总结
Manifest 文件概述
Manifest 文件是 Chrome Extension 的配置文件,定义了扩展的元数据、权限、资源和行为。它是每个扩展程序的必需文件,位于扩展根目录。
Manifest 版本演进
Manifest V3 核心配置
基本信息字段
"""
Manifest V3 基本配置结构
"""
import json
from typing import Dict, List, Optional, Union
class ManifestBuilder:
"""Manifest.json 构建器"""
def __init__(self, name: str, version: str):
self.manifest = {
"manifest_version": 3, # 必需:版本号
"name": name, # 必需:扩展名称
"version": version # 必需:版本号
}
def set_basic_info(self, description: str,
short_name: Optional[str] = None,
author: Optional[str] = None):
"""
设置基本信息
Args:
description: 扩展描述(最多132个字符)
short_name: 简短名称(最多12个字符)
author: 作者信息
"""
self.manifest["description"] = description[:132]
if short_name:
self.manifest["short_name"] = short_name[:12]
if author:
self.manifest["author"] = author
# 版本名称(用户友好的版本标识)
self.manifest["version_name"] = f"v{self.manifest['version']}"
return self
def set_icons(self, icon_sizes: Dict[int, str]):
"""
设置图标
Args:
icon_sizes: 图标尺寸和路径的映射
"""
self.manifest["icons"] = {
str(size): path for size, path in icon_sizes.items()
}
return self
def build(self) -> str:
"""构建并返回JSON字符串"""
return json.dumps(self.manifest, indent=2, ensure_ascii=False)
# 使用示例
builder = ManifestBuilder("My Extension", "1.0.0")
manifest = builder.set_basic_info(
description="这是一个强大的Chrome扩展",
short_name="MyExt",
author="Developer Name"
).set_icons({
16: "icons/icon-16.png",
32: "icons/icon-32.png",
48: "icons/icon-48.png",
128: "icons/icon-128.png"
}).build()
print("基本配置示例:")
print(manifest)Action 配置(原 Browser Action)
"""
Action 配置详解
"""
class ActionConfig:
"""Action 配置类"""
def __init__(self):
self.action_config = {}
def create_action(self, popup: Optional[str] = None,
default_title: Optional[str] = None,
default_icon: Optional[Union[str, Dict]] = None):
"""
创建 Action 配置
Args:
popup: 弹窗HTML文件路径
default_title: 鼠标悬停提示文本
default_icon: 图标配置
"""
config = {}
if popup:
config["default_popup"] = popup
if default_title:
config["default_title"] = default_title
if default_icon:
if isinstance(default_icon, str):
config["default_icon"] = default_icon
else:
config["default_icon"] = default_icon
return config
def action_examples(self):
"""Action 配置示例"""
examples = {
"简单弹窗": {
"action": {
"default_popup": "popup.html",
"default_title": "点击打开扩展"
}
},
"多尺寸图标": {
"action": {
"default_icon": {
"16": "icons/icon-16.png",
"24": "icons/icon-24.png",
"32": "icons/icon-32.png"
},
"default_title": "我的扩展"
}
},
"仅图标(无弹窗)": {
"action": {
"default_icon": "icon.png",
"default_title": "点击触发后台事件"
}
}
}
print("🎯 Action 配置示例:")
for name, config in examples.items():
print(f"\n{name}:")
print(json.dumps(config, indent=2, ensure_ascii=False))
return examples
# 使用示例
action = ActionConfig()
action.action_examples()权限系统详解
权限类型和声明
"""
Chrome Extension 权限管理
"""
class PermissionManager:
"""权限管理器"""
def __init__(self):
# 权限分类
self.permissions_catalog = {
"基础权限": [
"activeTab", # 访问当前活动标签页
"tabs", # 访问所有标签页信息
"storage", # 使用存储API
"contextMenus", # 添加右键菜单
"notifications", # 显示通知
"alarms", # 定时器
"clipboardRead", # 读取剪贴板
"clipboardWrite" # 写入剪贴板
],
"高级权限": [
"bookmarks", # 访问书签
"history", # 访问历史记录
"downloads", # 管理下载
"management", # 管理其他扩展
"cookies", # 访问Cookies
"webRequest", # 拦截网络请求
"webRequestBlocking", # 阻止网络请求
"proxy", # 控制代理设置
"debugger" # 使用调试器API
],
"主机权限": [
"<all_urls>", # 访问所有网站
"*://*.google.com/*", # 特定域名
"https://*/", # 所有HTTPS网站
"file:///*" # 本地文件
]
}
def create_permission_config(self, required_permissions: List[str],
optional_permissions: List[str] = None,
host_permissions: List[str] = None):
"""
创建权限配置
Args:
required_permissions: 必需权限列表
optional_permissions: 可选权限列表
host_permissions: 主机权限列表
"""
config = {
"permissions": required_permissions
}
if optional_permissions:
config["optional_permissions"] = optional_permissions
if host_permissions:
config["host_permissions"] = host_permissions
return config
def permission_best_practices(self):
"""权限最佳实践"""
practices = {
"最小权限原则": {
"说明": "只申请必要的权限",
"示例": {
"permissions": ["storage", "activeTab"],
"optional_permissions": ["downloads", "bookmarks"]
}
},
"动态权限请求": {
"说明": "运行时请求可选权限",
"代码": """
// 请求可选权限
chrome.permissions.request({
permissions: ['bookmarks'],
origins: ['https://www.example.com/']
}, (granted) => {
if (granted) {
// 权限已授予
console.log('权限已获得');
} else {
// 用户拒绝
console.log('权限被拒绝');
}
});"""
},
"权限检查": {
"说明": "使用前检查权限",
"代码": """
// 检查权限
chrome.permissions.contains({
permissions: ['bookmarks'],
origins: ['https://www.example.com/']
}, (result) => {
if (result) {
// 已有权限
performAction();
} else {
// 请求权限
requestPermission();
}
});"""
}
}
print("✅ 权限最佳实践:")
for practice, details in practices.items():
print(f"\n📌 {practice}")
print(f" {details['说明']}")
if '示例' in details:
print(f" 配置示例:")
print(f" {json.dumps(details['示例'], indent=6, ensure_ascii=False)}")
if '代码' in details:
print(f" 代码示例:{details['代码']}")
return practices
# 使用示例
pm = PermissionManager()
config = pm.create_permission_config(
required_permissions=["storage", "tabs"],
optional_permissions=["bookmarks", "downloads"],
host_permissions=["https://*.example.com/*"]
)
print("权限配置示例:")
print(json.dumps(config, indent=2))
pm.permission_best_practices()内容安全策略(CSP)
"""
内容安全策略配置
"""
class ContentSecurityPolicy:
"""CSP 配置类"""
def __init__(self):
self.csp_directives = []
def create_csp_config(self):
"""创建CSP配置示例"""
# Manifest V3 的CSP配置
csp_examples = {
"默认CSP(最严格)": {
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
}
},
"允许WASM": {
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
}
},
"沙盒页面CSP": {
"content_security_policy": {
"sandbox": "sandbox allow-scripts allow-forms allow-popups allow-modals; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self';"
}
}
}
print("🔒 CSP配置示例:")
for name, config in csp_examples.items():
print(f"\n{name}:")
print(json.dumps(config, indent=2, ensure_ascii=False))
# CSP违规处理
violation_handler = """
// 监听CSP违规事件
document.addEventListener('securitypolicyviolation', (e) => {
console.error('CSP违规:', {
blockedURI: e.blockedURI,
violatedDirective: e.violatedDirective,
originalPolicy: e.originalPolicy,
sourceFile: e.sourceFile,
lineNumber: e.lineNumber
});
// 报告违规到后台
chrome.runtime.sendMessage({
type: 'csp_violation',
details: {
uri: e.blockedURI,
directive: e.violatedDirective
}
});
});
"""
print("\n违规事件处理:")
print(violation_handler)
return csp_examples
# 使用示例
csp = ContentSecurityPolicy()
csp.create_csp_config()Background Scripts 配置
Service Worker(Manifest V3)
"""
Service Worker 配置(Manifest V3)
"""
class ServiceWorkerConfig:
"""Service Worker 配置类"""
def __init__(self):
self.config = {}
def create_background_config(self):
"""创建后台脚本配置"""
# Manifest V3 使用 Service Worker
v3_config = {
"background": {
"service_worker": "background.js",
"type": "module" # 可选:使用ES6模块
}
}
# Service Worker 示例代码
service_worker_code = """
// background.js - Service Worker
// 扩展安装事件
chrome.runtime.onInstalled.addListener((details) => {
console.log('扩展已安装:', details);
// 根据安装原因执行不同操作
switch(details.reason) {
case 'install':
console.log('首次安装');
// 设置默认值
chrome.storage.local.set({
installed: true,
version: chrome.runtime.getManifest().version
});
break;
case 'update':
console.log('扩展更新');
// 执行迁移逻辑
migrateData(details.previousVersion);
break;
case 'chrome_update':
console.log('Chrome更新');
break;
}
});
// 监听来自content script的消息
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('收到消息:', request, '来自:', sender);
// 异步处理
(async () => {
try {
const result = await handleMessage(request, sender);
sendResponse({success: true, data: result});
} catch (error) {
sendResponse({success: false, error: error.message});
}
})();
return true; // 保持消息通道开放
});
// 监听标签页更新
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
console.log('标签页加载完成:', tab.url);
}
});
// 定时任务
chrome.alarms.create('periodicTask', {
periodInMinutes: 30 // 每30分钟执行
});
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'periodicTask') {
performPeriodicTask();
}
});
// Service Worker 生命周期
self.addEventListener('activate', (event) => {
console.log('Service Worker 激活');
});
// 处理fetch事件(仅在特定场景下需要)
self.addEventListener('fetch', (event) => {
// 可以拦截和修改网络请求
console.log('Fetch事件:', event.request.url);
});
async function handleMessage(request, sender) {
// 消息处理逻辑
switch(request.type) {
case 'GET_DATA':
return await getData(request.key);
case 'SAVE_DATA':
return await saveData(request.key, request.value);
default:
throw new Error('未知的消息类型');
}
}
async function performPeriodicTask() {
// 定期执行的任务
console.log('执行定期任务');
}
"""
print("🔧 Service Worker 配置(Manifest V3):")
print(json.dumps(v3_config, indent=2))
print("\n示例代码:")
print(service_worker_code)
return v3_config, service_worker_code
# 使用示例
sw_config = ServiceWorkerConfig()
sw_config.create_background_config()Content Scripts 配置
内容脚本注入配置
"""
Content Scripts 配置详解
"""
class ContentScriptsConfig:
"""内容脚本配置类"""
def __init__(self):
self.scripts = []
def create_content_scripts_config(self):
"""创建内容脚本配置"""
# 静态声明方式
static_config = {
"content_scripts": [
{
"matches": ["*://*.example.com/*"], # 必需:URL匹配模式
"js": ["content.js"], # JavaScript文件
"css": ["content.css"], # CSS文件
"run_at": "document_end", # 注入时机
"all_frames": False, # 是否注入所有frame
"match_about_blank": False, # 是否匹配about:blank
"world": "ISOLATED" # 执行环境(V3新增)
}
]
}
# 注入时机选项
run_at_options = {
"document_start": "DOM构建开始时(最早)",
"document_end": "DOM构建完成时(默认)",
"document_idle": "页面空闲时或DOM完成后"
}
# 执行环境选项(Manifest V3)
world_options = {
"ISOLATED": "隔离环境(默认,有自己的全局作用域)",
"MAIN": "主世界(与页面共享全局作用域)"
}
# 动态注入方式(通过API)
dynamic_injection = """
// 动态注入内容脚本
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.scripting.executeScript({
target: {tabId: tabs[0].id},
files: ['content.js']
});
// 或者注入CSS
chrome.scripting.insertCSS({
target: {tabId: tabs[0].id},
files: ['content.css']
});
// 或者注入函数
chrome.scripting.executeScript({
target: {tabId: tabs[0].id},
func: injectedFunction,
args: ['arg1', 'arg2']
});
});
function injectedFunction(arg1, arg2) {
console.log('注入的函数执行:', arg1, arg2);
// 这个函数会在页面上下文中执行
}
"""
# 高级匹配模式
advanced_patterns = {
"包含特定路径": ["*://example.com/path/*"],
"排除特定页面": {
"matches": ["*://example.com/*"],
"exclude_matches": ["*://example.com/admin/*"]
},
"使用glob模式": {
"matches": ["*://example.com/*"],
"include_globs": ["*://example.com/user/*"],
"exclude_globs": ["*://example.com/*/private/*"]
}
}
print("📝 Content Scripts 静态配置:")
print(json.dumps(static_config, indent=2, ensure_ascii=False))
print("\n⏰ 注入时机选项:")
for key, desc in run_at_options.items():
print(f" {key}: {desc}")
print("\n🌍 执行环境选项 (V3):")
for key, desc in world_options.items():
print(f" {key}: {desc}")
print("\n💉 动态注入示例:")
print(dynamic_injection)
print("\n🎯 高级匹配模式:")
for name, pattern in advanced_patterns.items():
print(f" {name}:")
print(f" {json.dumps(pattern, indent=4, ensure_ascii=False)}")
return static_config
# 使用示例
cs_config = ContentScriptsConfig()
cs_config.create_content_scripts_config()Manifest V2 vs V3 差异
主要差异对比
"""
Manifest V2 与 V3 差异对比
"""
class ManifestMigration:
"""Manifest 迁移指南"""
def __init__(self):
self.differences = {}
def compare_versions(self):
"""比较V2和V3的差异"""
comparison_table = [
{
"特性": "后台脚本",
"Manifest V2": "background: { scripts: ['bg.js'], persistent: false }",
"Manifest V3": "background: { service_worker: 'sw.js' }",
"说明": "V3使用Service Worker替代后台页面"
},
{
"特性": "主机权限",
"Manifest V2": "permissions: ['<all_urls>']",
"Manifest V3": "host_permissions: ['<all_urls>']",
"说明": "V3将主机权限分离到单独字段"
},
{
"特性": "Action API",
"Manifest V2": "browser_action 或 page_action",
"Manifest V3": "action(统一)",
"说明": "V3统一为action API"
},
{
"特性": "Web Request API",
"Manifest V2": "完整的blocking webRequest",
"Manifest V3": "declarativeNetRequest(声明式)",
"说明": "V3限制了阻塞式网络请求"
},
{
"特性": "内容安全策略",
"Manifest V2": "可以使用unsafe-eval",
"Manifest V3": "禁止unsafe-eval(除WASM)",
"说明": "V3加强了安全限制"
},
{
"特性": "远程代码",
"Manifest V2": "可以加载远程代码",
"Manifest V3": "禁止远程代码执行",
"说明": "V3要求所有代码打包在扩展内"
}
]
print("📊 Manifest V2 vs V3 对比:\n")
print("| 特性 | Manifest V2 | Manifest V3 | 说明 |")
print("|------|-------------|-------------|------|")
for item in comparison_table:
print(f"| {item['特性']} | `{item['Manifest V2']}` | `{item['Manifest V3']}` | {item['说明']} |")
return comparison_table
def migration_guide(self):
"""V2到V3迁移指南"""
migration_steps = """
# Manifest V2 到 V3 迁移指南
## 1. 更新manifest_version
```json
// V2
"manifest_version": 2
// V3
"manifest_version": 32. 迁移后台脚本
// V2 - background.js
chrome.browserAction.onClicked.addListener(() => {
// 处理点击
});
// V3 - service-worker.js
chrome.action.onClicked.addListener(() => {
// 处理点击
});3. 更新权限声明
// V2
{
"permissions": [
"tabs",
"https://example.com/*"
]
}
// V3
{
"permissions": ["tabs"],
"host_permissions": ["https://example.com/*"]
}4. 替换Web Request API
// V2 - 使用webRequest
chrome.webRequest.onBeforeRequest.addListener(
(details) => {
return {cancel: true};
},
{urls: ["*://example.com/*"]},
["blocking"]
);
// V3 - 使用declarativeNetRequest
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{
id: 1,
priority: 1,
action: {type: "block"},
condition: {urlFilter: "example.com"}
}]
});5. 处理Service Worker生命周期
// V3 - Service Worker会自动休眠
// 使用chrome.storage而不是全局变量
let data; // ❌ 避免使用
// ✅ 使用storage
chrome.storage.local.set({data: value});
chrome.storage.local.get(['data'], (result) => {
// 使用数据
});"""
print(migration_steps)
return migration_steps
使用示例
migration = ManifestMigration()
migration.compare_versions()
migration.migration_guide()
## 完整Manifest示例
### 实战项目配置
```python
"""
完整的Manifest配置示例
"""
def create_complete_manifest():
"""创建完整的Manifest V3配置"""
complete_manifest = {
"manifest_version": 3,
"name": "Advanced Chrome Extension",
"version": "2.0.0",
"description": "功能完整的Chrome扩展示例",
"author": "Your Name",
# 图标
"icons": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
# Action配置
"action": {
"default_popup": "popup/popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png"
},
"default_title": "点击查看选项"
},
# 后台Service Worker
"background": {
"service_worker": "background/service-worker.js",
"type": "module"
},
# 内容脚本
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/content.js"],
"css": ["content/content.css"],
"run_at": "document_idle",
"all_frames": False
}
],
# 权限
"permissions": [
"storage",
"tabs",
"notifications",
"alarms",
"contextMenus"
],
# 可选权限
"optional_permissions": [
"bookmarks",
"history",
"downloads"
],
# 主机权限
"host_permissions": [
"https://*/*",
"http://localhost/*"
],
# 选项页面
"options_page": "options/options.html",
# Web可访问资源
"web_accessible_resources": [
{
"resources": ["images/*.png", "styles/*.css"],
"matches": ["<all_urls>"]
}
],
# 内容安全策略
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'"
},
# 命令快捷键
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"mac": "Command+Shift+Y"
},
"description": "打开扩展弹窗"
},
"toggle-feature": {
"suggested_key": {
"default": "Ctrl+Shift+U"
},
"description": "切换功能开关"
}
},
# 最小Chrome版本
"minimum_chrome_version": "88",
# 默认语言
"default_locale": "en"
}
print("📦 完整的Manifest V3配置示例:")
print(json.dumps(complete_manifest, indent=2, ensure_ascii=False))
# 配置验证脚本
validation_script = """
// 验证Manifest配置
async function validateManifest() {
const manifest = chrome.runtime.getManifest();
console.log('Manifest验证:');
console.log('- 版本:', manifest.version);
console.log('- 权限:', manifest.permissions);
console.log('- 主机权限:', manifest.host_permissions);
// 检查必需字段
const required = ['manifest_version', 'name', 'version'];
const missing = required.filter(field => !manifest[field]);
if (missing.length > 0) {
console.error('缺少必需字段:', missing);
return false;
}
// 检查版本格式
const versionPattern = /^\\d+\\.\\d+\\.\\d+$/;
if (!versionPattern.test(manifest.version)) {
console.error('版本格式错误:', manifest.version);
return false;
}
console.log('✅ Manifest验证通过');
return true;
}
validateManifest();
"""
print("\n验证脚本:")
print(validation_script)
return complete_manifest
# 创建完整配置
create_complete_manifest()注意事项
- 版本兼容性: 确保目标Chrome版本支持所使用的API
- 权限申请: 遵循最小权限原则,避免过度申请
- 安全策略: 严格遵守CSP规则,确保扩展安全
- 迁移计划: V2扩展需要尽快迁移到V3
本章小结
本章深入学习了Chrome Extension的Manifest文件配置:
- 基本配置: 掌握了必需字段和基本信息设置
- 权限系统: 理解了权限类型和申请策略
- 后台脚本: 学习了Service Worker的配置方法
- 内容脚本: 了解了内容脚本的注入配置
- 版本差异: 对比了V2和V3的主要区别
- 迁移指南: 掌握了从V2到V3的迁移方法
下一章将学习Content Scripts的详细开发。
