Chrome Extensions (谷歌插件)开发入门

1,212 阅读17分钟

Chrome extensions 概况

什么是 Chrome extensions

浏览器扩展程序 extensions 是一个小型的程序,它可以使用浏览器提供的 API 和 web 技术以增强浏览器功能。它们让用户可以通过多种方式定制 Chrome 的功能和行为,例如生产力工具、信息聚合、 丰富网页内容等

官方说明:

Extensions are made of different, but cohesive, components. Components can include background scriptscontent scripts, an options pageUI elements and various logic files. Extension components are created with web development technologies: HTML, CSS, and JavaScript.. 扩展由不同但有凝聚力的组件组成。组件可以包括背景脚本、内容脚本、选项页面、UI 元素和各种逻辑文件。扩展组件是使用 Web 开发技术创建的:HTML、CSS 和 JavaScript。

Chrome插件官方称为Chrome扩展(Chrome Extension),真正意义上的Chrome插件是更底层的浏览器功能扩展。本文遵循官方文档,称为Chrome扩展。

Chrome extensions 能干什么

增强浏览器功能,轻松实现属于自己的“定制版”浏览器。

参考官方文档:Extension development overview Chrome插件提供了很多实用API供我们使用,包括但不限于:

  • 书签控制;
  • 自定义扩展用户界面
  • 下载控制;
  • 窗口控制;
  • 标签控制;
  • 网络请求控制,各类事件监听;
  • 自定义原生菜单;
  • 完善的通信机制;

请访问Chrome 应用商店查看已发布扩展程序示例。

截屏2022-09-28 15.09.13.png

安装 Chrome 拓展

大多数 Chrome 用户从 Chrome 应用商店获取扩展程序,也可以在本地机器上安装

本地机器上安装

  1. 在浏览器中导航到 chrome://extensions。还可以通过单击多功能框右上角的 Chrome 菜单、将鼠标悬停在更多工具上并选择扩展来访问此页面

截屏2022-09-28 16.05.45.png

  1. 选中开发者模式,单击加载已解压的扩展程序

截屏2022-09-28 16.06.24.png

Hello World

hello world 代码仓库 v1

安装示例:

截屏2022-09-28 16.41.50.png

架构概述

扩展程序依其功能的不同,项目的结构和所包含的文件类型也不同,但是它们一般都由以下部分构成:

  • Manifest:每一个扩展程序都必须有一个配置清单 manifest.json 文档,在其中清楚地包含该扩展程序的相关信息和所需使用的权限。

  • Service worker:Service worker是扩展的事件处理程序;它包含对浏览器事件的侦听器。 它处于休眠状态,直到触发事件然后执行指示的逻辑;它仅在需要时加载并在空闲时卸载。Service Worker 可以访问所有 Chrome API,只要它在 manifest.json 中声明了所需的权限

  • Content Script:内容脚本,它可以植入页面,一般用于读取页面内容,或向页面插入内容。

  • UI element ui 包括: Action badgePopupTooltipClick EventOmniboxContext menuCommandsOverride pagesNotifications

Manifest V3

每一个扩展程序都需要有一个配置清单 manifest.json 文档,它提供了关于扩展程序的基本信息,例如所需的权限、名称、版本等。

当前配置清单类型最新的版本是 Manifest V3,遵循该配置清单版本的扩展程序会更注重安全和用户的隐私保护,在性能方面也会得到提升,同时开发简易性和功能实现也会更佳。

Chrome 88 版本开始支援配置清单为 Manifest V3 版本的扩展程序,Chrome 网上应用店从 2021 年 1 月开始支持分发配置清单为 Manifest V3 版本的扩展程序。

截屏2022-09-29 14.20.06.png

其中,manifest_versionnameversion3个是必不可少的,descriptionicons是推荐的。

Manifest file format

{
  // Required
  "manifest_version": 3,
  "name": "My Extension",
  "version": "versionString",

  // Recommended
  "action": {...},
  "default_locale": "en",
  "description": "A plain text description",
  "icons": {...},

  // Optional
  "author": ...,
  "automation": ...,
  "background": {
    // Required
    "service_worker": "background.js",
    // Optional
    "type": ...
  },
  "chrome_settings_overrides": {...},
  "chrome_url_overrides": {...},
  "commands": {...},
  "content_capabilities": ...,
  "content_scripts": [{...}],
  "content_security_policy": {...},
  "converted_from_user_script": ...,
  "cross_origin_embedder_policy": {"value": "require-corp"},
  "cross_origin_opener_policy": {"value": "same-origin"},
  "current_locale": ...,
  "declarative_net_request": ...,
  "devtools_page": "devtools.html",
  "differential_fingerprint": ...,
  "event_rules": [{...}],
  "externally_connectable": {
    "matches": ["*://*.example.com/*"]
  },
  "file_browser_handlers": [...],
  "file_system_provider_capabilities": {
    "configurable": true,
    "multiple_mounts": true,
    "source": "network"
  },
  "homepage_url": "https://path/to/homepage",
  "host_permissions": [...],
  "import": [{"id": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}],
  "incognito": "spanning, split, or not_allowed",
  "input_components": ...,
  "key": "publicKey",
  "minimum_chrome_version": "versionString",
  "nacl_modules": [...],
  "natively_connectable": ...,
  "oauth2": ...,
  "offline_enabled": true,
  "omnibox": {
    "keyword": "aString"
  },
  "optional_host_permissions": ["..."],
  "optional_permissions": ["tabs"],
  "options_page": "options.html",
  "options_ui": {
    "page": "options.html"
  },
  "permissions": ["tabs"],
  "platforms": ...,
  "replacement_web_app": ...,
  "requirements": {...},
  "sandbox": [...],
  "short_name": "Short Name",
  "storage": {
    "managed_schema": "schema.json"
  },
  "system_indicator": ...,
  "tts_engine": {...},
  "update_url": "https://path/to/updateInfo.xml",
  "version_name": "aString",
  "web_accessible_resources": [...]
}

API 概览

Chrome为扩展提供了许多特殊用途的 API,例如 chrome.runtimechrome.alarms。 可以通过API 参考查看Chrom extension提供的api

API 声明权限

要使用 chrome.* API,您的扩展程序必须在manifest的权限字段中声明。 扩展可以请求三类权限:

  • permissions 如 Storage
  • optional_permissions 可选的权限
  • host_permissions 主机权限 实例:
"permissions": [
  "tabs",
  "bookmarks",
  "unlimitedStorage"
],
"optional_permissions": [
  "unlimitedStorage"
],
"host_permissions": [
  "http://www.blogger.com/",
  "http://*.google.com/"
],

可通过权限列表查看哪些api需要注册权限

Service Workers

Manifest V2 版本中,后台页面 background pages 是扩展程序中的一个独立页面,一般设置事件监听,以响应用户的操作,但是它会长期驻留后台影响性能。

Service Workers 特点

在 Manifest V3 版本中,后台脚本迁移到 Service Workers 中运行以提供性能,其中有两个特点:

  • Service Workers 在执行事件处理函数后终止,并在新的事件触发下重新运行

  • 它是一种 JavaScript Worker无法直接访问 DOM

💡 Service Worker 是浏览器完全独立于网页运行的脚本。除了以上的特点,还要注意以下的相关事项:

  • 它是一种可编程网络代理,让您能够控制页面所发送网络请求的处理方式。

  • 它广泛地利用了 promise 进行异步操作。

如果需要使用 service workers 时需要在配置清单 manifest.json 的选项 background.service_worker 中声明注册,属性值指定需要执行的一个 JavaScript 文档的路径(它必须在项目的根目录下)

{
  "name": "Awesome Test Extension",
  ...
  "background": {
    "service_worker": "background.js",
    "type": "module" // 
  },
  ...
}

Service Workers注意事项

  1. 事件注册在Service Workers脚本的顶层
// background.js
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
  chrome.action.setBadgeText({ text: badgeText });
  
  chrome.action.onClicked.addListener(handleActionClick);
});

// true
chrome.action.onClicked.addListener(handleActionClick);

  1. Service Workers 基于监听事件-响应模型来执行。是 short-lived,有别于long-lived 在Manifest V2中:
let savedName = undefined;

chrome.runtime.onMessage.addListener(({ type, name }) => {
  if (type === "set-name") {
    savedName = name;
  }
});

chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.sendMessage(tab.id, { name: savedName });
});

在Manifest V3中实现此功能,需要借助Storage APIs

// background.js
chrome.runtime.onMessage.addListener(({ type, name }) => {
  if (type === "set-name") {
    chrome.storage.local.set({ name });
  }
});

chrome.action.onClicked.addListener((tab) => {
  chrome.storage.local.get(["name"], ({ name }) => {
    chrome.tabs.sendMessage(tab.id, { name });
  });
});
  1. 在 Service Workers 中不能使用setTimeout or setInterval,这些 API 在Service Workers 中可能会失败,因为调度程序将在Service Workers终止时取消计时器
// background.js

// This worked in Manifest V2.
const TIMEOUT = 3 * 60 * 1000; // 3 minutes in milliseconds
setTimeout(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
}, TIMEOUT);

需要使用Alarms API

// background.js
chrome.alarms.create({ delayInMinutes: 3 });

chrome.alarms.onAlarm.addListener(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
});
  1. 无权访问 DOM。Service Worker 不再提供 XMLHttpRequest,而是支持更现代的 fetch()

Content scripts

Content scripts 是运行在web 上下文的脚本。

它可以访问页面的 DOM 对象,还可以以通过信息传递 message passing的方式与扩展程序进行通讯,可以将它看作是页面与扩展程序之间的桥梁角色。

Content scripts 可访问API

Content scripts可以直接访问以下 chrome API:

独立运行环境

内容脚本 content script 在一个独立的环境中执行(私有作用域),因此页面和扩展程序都无法访问内容脚本 content script 的变量,可以避免与页面或扩展程序的脚本发生冲突。 注入脚本

注入脚本

内容脚本可以静态声明或以编程方式注入

静态注入

静态声明的脚本在“content_scripts”字段下的清单中注册。它们可以包括 JavaScript 文件、CSS 文件或两者兼有。所有自动运行的内容脚本都必须指定匹配模式。

{
 "name": "My extension",
 "content_scripts": [
   {
     "matches": ["https://*.nytimes.com/*"],
     "css": ["my-styles.css"],
     "js": ["content-script.js"]
   }
 ],
}

content_scripts 字段中的值的含义

NameTypeDescription
matchesarray of stringsRequired.  
cssarray of stringsOptional.  
jsarray of stringsOptional.  
match_about_blankbooleanOptional.  
match_origin_as_fallbackbooleanOptional.

动态注入

动态注入脚本需要对应的主机权限和 scripting权限。

主机权限可以通过将它们作为扩展清单的一部分请求来授予(请参阅 host_permissions),也可以通过 activeTab 临时授予。

在配置清单 manifest.json 的选项 permissions 中声明 activeTab 权限,可以临时获取许可,以访问当前激活的标签页,并在当前页面使用 tabs 相关的 API

由于扩展程序的很多使用场景都只需临时访问当前激活的标签页,而不是针对特定的网页,所以与基于 URL 规则获取的永久访问权限相比,该类型的权限更常用。该权限基于用户的主动请求临时获取的(例如通过点击 Action 控件),而且仅限制在当前激活页面,相对而言更安全。

示例:

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

background.js:

function injectedFunction() {
  document.body.style.backgroundColor = 'orange';
}

chrome.action.onClicked.addListener((tab) => {
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    func: injectedFunction
  });
});

官方文档

源码参考:

通信 Message passing

由于内容脚本在网页上下文而不是扩展程序的上下文中运行,因此它们通常需要某种方式与扩展程序的其余部分进行通信。

Chrome 除了提供简单的 API 进行一次性的请求-响应通讯(one-time requests),也提供复杂的 API 进行长连接通讯(long-lived connections),还可以基于 ID 进行跨扩展程序的通讯。

Simple one-time requests

使用runtime.sendMessage or tabs.sendMessage 进行单次请求,这两个方法可以设置回调函数,默认接收返回的响应数据作为参数。 发送消息

// content script发送消息

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});
// 向content script中发送消息

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});

接收方接收消息

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting === "hello")
      sendResponse({farewell: "goodbye"});
  }
);

Long-lived connections

可以使用runtime.connect or tabs.connect 为内容脚本 content script 和扩展程序之间建立一个长连接(可以为信息通道 channel 设置名称,以区别多个通道)。

使用以上方法创建通道后,会返回一个 runtime.Port 端口对象,其中包括了关于信息通道的相关方法和属性,然后就可以通过该通道发送 portObj.postMessage() 和接收 portObj.onMessage.addListener() 信息。

// 在页面脚本 content script 建立长连接的信息通道
let port = chrome.runtime.connect({name: "knockknock"});
// 通过该端口发送信息
port.postMessage({joke: "Knock knock"});
// 设置事件监听器,通过该端口接收信息,将接收到的信息作为入参
port.onMessage.addListener(function(msg) {
  if (msg.question === "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question === "Madame who?")
    port.postMessage({answer: "Madame... Bovary"});
});

类似地,如果在扩展程序中建立长连接发送消息时,使用方法 chrome.tabs.connect(),需要指定请求是发送给哪个特定的 tab 标签页。

信息通道是双向的,因此除了发起端创建端口,还需要在接收端使用runtime.onConnect 响应通道连接请求(在内容脚本 content script 和扩展程序中一样)。当通道发起端口调用 connect 方法时,接收端的监听器就会调用回调函数,它将相应的 runtime.Port 端口对象作为入参,然后可以使用该端口在通道中发送和接收消息,这样通道两端的接口就可以相互接收和发送信息了。

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name === "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke === "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer === "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer === "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});

Cross-origin XMLHttpRequest

常规网页可以使用 XMLHttpRequest 对象从远程服务器发送和接收数据,但它们受到同源策略的限制。

content script以植入网页的方式运行,因此content script本也受制于相同的源策略。 而扩展程序的后台脚本 background scripts 就不受此限制,只需要在 host_permissions 中声明后就可以访问相应的远程服务器;

如果是通过 fetch() 的方式获取扩展程序内部的静态资源,则不需要声明权限。

通过将主机或主机匹配模式(或两者)添加到清单文件的 host_permissions 部分,扩展可以请求访问其源之外的远程服务器。

声明主机权限

{
  "name": "My extension",
  ...
  "host_permissions": [
    "https://www.google.com/"
  ],
  ...
}

发送跨域请求

由于内容脚本 content scripts 受到同源政策的限制,可以通过信息传递 message passing 借助扩展程序来 fetch 相应的服务器获取数据。

sendMessage 与后台脚本通信

chrome.runtime.sendMessage(
    {contentScriptQuery: 'queryPrice', itemId: 12345},
    price => ...);

后台脚本进行事件监听

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      if (request.contentScriptQuery == 'queryPrice') {
        var url = 'https://www.google.com/?itemId=' +
            encodeURIComponent(request.itemId);
        fetch(url)
            .then(response => response.text())
            .then(text => parsePrice(text))
            .then(price => sendResponse(price))
            .catch(error => ...)
        return true;  // Will respond asynchronously.
      }
    });

chrome.alarms

使用 chrome.alarms API 使代码定期运行或在未来的指定时间运行。

要使用chrome.alarms.* API,首先需要在manifest.json文件中声明alarms授权如下:

{
    "permissions": [
        "alarms"
    ],
}

Alarm 类型

chrome.alarms.create()方法可以创建一个alarm,Alarm包含以下字段:

  • name ( string ) 该定时器的名称。
  • scheduledTime 该定时器计划的触发时间。由于性能原因,定时器可能会延迟至该时间后的任意时间。
  • periodInMinutes ( optional ) 如果不是 null 的话,该定时器会重复触发,每隔 periodInMinutes 分钟触发。

chrome.alarms 方法

chrome.alarms API中的常用方法:

  • 创建一个alarm
chrome.alarms.create(
  name?: string,
  alarmInfoAlarmCreateInfo,
)

AlarmCreateInfo 类型说明:

  1. delayInMinutes: onAlarm 事件应该触发的时间长度(以分钟为单位)
  2. periodInMinutes: 指定重复触发的时间间隔,如果未设置,则警报只会触发一次。
  3. when: 警报应该触发的时间,以历元过去的毫秒数为单位(例如 Date.now() + n)
  • 获取指定名字的alarm
chrome.alarms.get(
  name?: string,
  callback?: function,
)
  • 获取所有alarm
chrome.alarms.getAll(  
  callback?: function,  
)
  • 通过名字删除alarm
chrome.alarms.clear(  
  name?: string,  
  callback?: function,  
)
  • 清除所有alarm
chrome.alarms.clearAll(
  callback?: function,
)

监听alarm发生的事件,用于event page

chrome.alarms.onAlarm.addListener(  
  callbackfunction,  
)

回调函数中的alarm就是触发事件的alarm对象。

Storage

该 API 已经过优化以满足扩展的特定存储需求。它提供与 localStorage API 相同的存储功能,但主要区别如下:

  • 使用 chrome.storage.sync 相关方法,就可以利用 Chrome 的同步功能,实现同一账户下的扩展程序数据在多个设备之间同步
  • 批量的读取和写入数据操作是异步执行的,因此与 localStorage 引起的阻塞和串行相比操作更快
  • 存储的数据类型可以是对象,而 localStorage 只允许存储字符串

使用

先声明权限

{
  "name": "My extension",
  ...
  "permissions": [
    "storage"
  ],
  ...
}

sync 同步存储

chrome.storage.sync.set({key: value}, function() {
  console.log('Value is set to ' + value);
});

chrome.storage.sync.get(['key'], function(result) {
  console.log('Value currently is ' + result.key);
});

本地存储

chrome.storage.local.set({key: value}, function() {
  console.log('Value is set to ' + value);
});

chrome.storage.local.get(['key'], function(result) {
  console.log('Value currently is ' + result.key);
});

参考

Storage API 参考

UI

与 Chrome 的用户界面 (UI) 一样,extension UI 应该是有目的的和最小的。extension允许用户自定义或增强用户的浏览体验,而不会分散注意力。

action

Action API 控制extension的操作(工具栏图标)。它可以在单击时打开一个popup或当点击的时候触发某些功能。 截屏2022-10-13 10.59.01.png 要使用 Action APImanifest必须包含一个“action”键。

{
  "name": "My Awesome action MV3 Extension",
  ...
  "action": {
    ...
  }
  ...
}

通过default_icon 字段设置extension 位于工具栏右侧 的icon。可以设置多个尺寸的图片

{
  "name": "My Awesome Extension",
  ...
  "action": {
    "default_icon": {
      "16": "extension_toolbar_icon16.png",
      "32": "extension_toolbar_icon32.png"
    }
  }
  ...
}

icons

可以通过manifest 中的icons字段设置工具栏以外的其他icon

 "icons": {
    "16": "extension_icon16.png",
    "32": "extension_icon32.png",
    "48": "extension_icon48.png",
    "128": "extension_icon128.png"
  }
Icon SizeIcon Use
16x16extension pages 和上下问菜单中的icon
32x32Windows 计算机通常需要这种尺寸
48x48extension 管理页面中展示
128x128Chrome Web Store 展示

其他用户界面功能

Action badge

Action badge 在操作图标顶部显示彩色banner

截屏2022-10-13 11.33.23.png

可以使用以下api设置:

chrome.action.setBadgeText({text: '14'});
chrome.action.setBadgeBackgroundColor({color: '#4688F1'});

Popup

Popup是HTML文件,当点击action icon时可以展示该页面。

它可以包含样式表和脚本标签的链接,但不允许内联 JavaScript。 popup在“action”键下的清单中注册

{
  "name": "Drink Water Event",
  ...
  "action": {
    "default_popup": "popup.html"
  }
  ...
}

也可以通过action.setPopup 动态设置

chrome.storage.local.get('signed_in', (data) => {
  if (data.signed_in) {
    chrome.action.setPopup({popup: 'popup.html'});
  } else {
    chrome.action.setPopup({popup: 'popup_sign_in.html'});
  }
});

Tooltip

将鼠标悬停在操作图标上时,使用Tooltip向用户提供简短描述或说明。

截屏2022-10-13 11.52.28.png

{
  "name": "Tab Flipper",
  ...
  "action": {
    "default_title": "学习chrome extension 演示demo"
  }
...
}

Click Event

通过 action.onClicked.addListener给action注册点击事件,但是如果设置了popup不会生效

chrome.action.onClicked.addListener(function(tab) {
  chrome.action.setTitle({tabId: tab.id, title: "You are on tab:" + tab.id});
});

Omnibox

{
  "name": "Omnibox New Tab Search",
  ...
  "omnibox": { "keyword" : "nt" },
  "default_icon": {
    "16": "newtab_search16.png",
    "32": "newtab_search32.png"
  }
  ...
}

Context menu

可以使用ContextMenus API 设置 Context menu。 现在manifest中注册 contextMenus权限

{
  "permissions": [
    "contextMenus",
  ]
}

通过contextMenus.create()创建 Context menu。通常在runtime.onInstalled 的回调函数中完成

chrome.runtime.onInstalled.addListener(async () => {
  for (let [tld, locale] of Object.entries(tldLocales)) {
    chrome.contextMenus.create({
      id: tld,
      title: locale,
      type: 'normal',
      contexts: ['selection'],
    });
  }
});

Commands

扩展可以定义特定的 Commands API 并将它们绑定到一个组合键。在“commands”键下的清单中注册一个或多个快捷方式

{
   "commands": {
    "go-google": {
      "suggested_key": {
        "default": "Ctrl+Shift+L",
        "mac": "Command+Shift+L"
      },
      "description": "谷歌"
    },
    "go-baidu": {
      "suggested_key": {
        "default": "Ctrl+Shift+P",
        "mac": "Command+Shift+P"
      },
      "description": "百度"
    }
  }
}

然后在 service worker中通过进行事件监听:

chrome.commands.onCommand.addListener(command => {
  if (command === 'go-google') {
    chrome.tabs.create({ url: "https://www.google.com" });
  } else {
    chrome.tabs.create({ url: "https://www.baidu.com" });
  }
});

参考:developer.chrome.com/docs/extens…

Override pages

extension 允许用户通过Override pages历替换史记录、新选项卡或书签页面, 像popup一样,但不允许内联 JavaScript。

在manifestchrome_url_overrides字段中注册覆盖页面,只能注册一个页面

{
  "name": "Awesome Override Extension",
  ...

  "chrome_url_overrides" : {
    "newtab": "override_page.html"// "history": "history.html"
  },
  ...
}

Notifications

您可以通过直接在系统托盘中显示通知来向用户传达相关信息 需要先注册 notifications 权限:

// manifest.json
{ 
  "name": "Drink Water Event Popup",
...
  "permissions": [
    "alarms",
    "notifications",
    "storage"
  ],
 ...
}

调用notifications.create发送通知:

 chrome.notifications.create({
    type: 'basic',
    iconUrl: 'popup/stay_hydrated.png',
    title: '温馨提示',
    message: '太累了就休息会儿吧',
    buttons: [
      { title: '好的' }
    ],
    priority: 0
  });

History

使用 chrome.history API可以操作浏览器访问过的页面记录。

可以在浏览器的历史记录中添加,删除和查询URL

权限申请

 "permissions": ["history"]

方法使用

  1. chrome.history.search(object query, function callback) 在历史记录中搜索与查询匹配的每个页面的上次访问记录

注意 query 参数,text 字段是必不可少的,但是 text 可以留空,相当于一个 keyword

    const query = {
        text: ''
    };
    chrome.history.search(query, (res) => {
        console.log(res);
    })
  1. chrome.history.getVisits(object details, function callback) 检索某个 URL 的访问信息
const details = {
    url: 'http://www.baidu.com'
};
chrome.history.getVisits(details, (res) => {
    const arr = res.slice(0,3).map((item) => {
        return item
    });
    let htm = '';
    arr.forEach(element => {
        htm += `<p>transition: ${element.transition}</p>`;
    });
    document.querySelector('#scroll').innerHTML = htm;
})
  1. chrome.history.addUrl(object details, function callback) 使用 transition type link 向历史记录中添加一条记录
const details = {
    url: 'http://ptbird.cn/chrome_extensions_aaa_2-s_as'
};
chrome.history.addUrl(details, (res) => {
    console.log(res);
})
  1. chrome.history.deleteUrl(object details, function callback) 从历史记录中删除所有出现的给定URL
const details = {
    url: 'http://www.baidu.com'
};
chrome.history.deleteUrl(details, (res) => {
    console.log(res);
})
  1. chrome.history.deleteRange(object range, function callback) 从历史记录中删除指定日期范围内的所有项目。除非所有访问都在此范围内,否则页面不会从历史记录中删除。
const startTime = Date.now() - 50000;
const endTime = Date.now();
const query = {
    startTime,
    endTime
};
chrome.history.deleteRange(query, (res) => {
    console.log(res);
})
```Javascript
6、chrome.history.deleteAll(function callback)
删除历史记录中的所有项目。
```Javascript
chrome.history.deleteAll((res) => {
       console.log(res);
    })

Bookmarks

使用 chrome.bookmarks 能够创建、组织和操作书签,能够自定义操作书签的页面。

权限申请

如果要使用 chrome.bookmarks 需要在 manifest 的 permissions 中增加 bookmarks 权限申请。

  "permissions": ["tabs", "bookmarks", "storage"]

书签组织形式

书签以树形式组织,树中的每个节点都是书签或文件夹(有时称为组)。

树中的每个节点都由 bookmarks.BookmarkTreeNode 对象表示。

整个 chrome.bookmarks API 都是使用 BookmarkTreeNode 属性。例如,当调用 bookmarks.create 时,传入新节点的父节点(parentId),并且可以选择传入节点的索引、标题和 URL 属性。

注意:无法使用 chrome.bookmarks API 添加或删除根文件夹中的条目。无法重命名、移动或删除特殊的 “书签栏” 和 “其他” 文件夹。

方法示例

  1. chrome.bookmarks.getTree(function callback) 获取整个书签栏的树
btn1.onclick = () => {
    chrome.bookmarks.getTree((res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.getSubTree(string id, function callback) 获取某个节点的子树
btn2.onclick = () => {
    chrome.bookmarks.getSubTree(tree[0].children[0].children[0].id, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.get(string or array of string idOrIdList, function callback) 获取节点信息

需要注意的是,无论是什么 get 方法,每次都是返回一个 Array 类型,即使请求的是节点,返回的也是 Array

btn3.onclick = () => {
    chrome.bookmarks.get("5", (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.getChildren(string id, function callback) 获取所有的子节点
btn4.onclick = () => {
    chrome.bookmarks.getChildren("5", (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.getRecent(integer numberOfItems, function callback) 获取最近的书签
btn5.onclick = () => {
    chrome.bookmarks.getRecent(5, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.search(string or object query, function callback) 搜索书签
<div class="col-md-12">
  <textarea id="textarea" cols="30" rows="1">postbird</textarea>
  <button id="btn6" class="btn btn-primary">search</button>
</div>
btn6.onclick = () => {
    const value = document.querySelector('#textarea').value;
    chrome.bookmarks.search(value, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.create(object bookmark, function callback) 创建一个书签,在指定的 parentId 下创建书签或文件夹。如果 url 为N ULL 或没有该字段,会创建一个文件夹。
btn7.onclick = () => {
    const title = document.querySelector('#title').value;
    const url = document.querySelector('#url').value;
    const obj = {
        parentId: '5',
        title,
        url,
    }
    chrome.bookmarks.create(obj, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.move(string id, object destination, function callback) 移动书签,注意不能移动到 0 这个 root 节点中,因为不允许任何修改 root 节点的行为
btn8.onclick = () => {
    const dest = {
        parentId: '1'
    }
    chrome.bookmarks.move('48', dest, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.update(string id, object changes, function callback) 更新书签或文件夹的属性。仅指定要更改的属性; 未指定的属性将保持不变。注意:**目前,仅支持 title 和 url。
btn9.onclick = () => {
    const title = document.querySelector('#titleUpdate').value;
    const url = document.querySelector('#urlUpdate').value;
    const obj = {
        title,
        url,
    }
    chrome.bookmarks.update('48', obj, (res) => {
        console.log(res);
    })
};
  1. chrome.bookmarks.remove(string id, function callback) 删除书签或空书签文件夹。
chrome.bookmarks.remove('48', (res) => {
        console.log(res);
    })

11、chrome.bookmarks.removeTree(string id, function callback) 删除整个书签树

chrome.bookmarks.removeTree('5', (res) => {
        console.log(res);
    })

参考:

小茗同学

官网