💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
参考博客[https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html](https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html) Google Chrome扩展最常用的可视界面为如下两种形式(两者是互斥的): 它们的UI部分有图标(icon)、提示(tooltip)徽章(badge)、弹出框(popup)组成 * ## **<span style="color:red">browser actions</span>** 这样的Google Chrome扩展可以适用于**任何**页面。图标往往位于浏览器地址栏**外**右侧。点击图标将弹出窗口。 ![](https://img.kancloud.cn/a6/f3/a6f325b4e2dda15e9748ea63cbc1f7b7_122x29.png) >[info]这个图标与浏览器相关,只要安装了该Chrome扩展的浏览器,就会显示该图标。 鼠标悬浮到图标上会给出提示信息,鼠标点击图标会弹出popup页面。图标还可以根据条件设置不同的徽章(Badge),提示用户不同的条件状态 启用他必须在manifest.json文件中注册如下: ``` { ... "browser_action": { "default_icon": "icons/icon.png",//图标(icon)也可以调用setIcon()方法 // 图标悬停时的标题,可选 "default_title": "这是一个示例Chrome插件",//对应提示(tooltip)还可以调用`setTitle()`方法设置此方法 //单击图标打开popup,焦点离开又立即关闭,所以popup页面的生命周期一般很短,需要长时间运行的代码千万不要写在popup里面 "default_popup": "popup.html"//弹出框(popup) //徽章(badge)badge无法通过配置文件来指定,必须通过代码实现,设置badge文字和颜色可以分别使用`setBadgeText()`和`setBadgeBackgroundColor()` }, ... } ``` 设置badge ``` chrome.browserAction.setBadgeText({text: 'new'}); chrome.browserAction.setBadgeBackgroundColor({color: [255, 0, 0, 255]}); ``` 效果 ![](https://img.kancloud.cn/d3/f0/d3f0e6fdb21b2ca14f8bbce3c2da72be_179x122.png) * ## **<span style="color:red">page actions</span>** 这样的Google Chrome扩展只能作用于**某一**页面,当打开该页面时触发该Google Chrome扩展,关闭页面则Google Chrome扩展也随之消失。图标往往位于浏览器地址栏**内**右端。还有个不同点是没有点亮时是灰色的,点亮了才是彩色的(一般通过background 里的事件api控制) ``` chrome.pageAction.show(tabId); //显示图标; chrome.pageAction.hide(tabId); //隐藏图标; ``` 示例(只有打开百度才显示图标): ``` // manifest.json { "page_action": { "default_icon": "img/icon.png", "default_title": "我是pageAction", "default_popup": "popup.html" }, "permissions": ["declarativeContent"] } // background.js chrome.runtime.onInstalled.addListener(function(){ chrome.declarativeContent.onPageChanged.removeRules(undefined, function(){ chrome.declarativeContent.onPageChanged.addRules([ { conditions: [ // 只有打开百度才显示pageAction new chrome.declarativeContent.PageStateMatcher({pageUrl: {urlContains: 'baidu.com'}}) ], actions: [new chrome.declarativeContent.ShowPageAction()] } ]); }); }); ``` ![](https://img.kancloud.cn/44/1e/441e28084d978bda3b4eb740b3c7c075_590x115.gif) ## **此外,Google Chrome扩展还支持其他的可视界面:** * ## **<span style="color:red">context menu</span>**,右键菜单 >通过开发Chrome插件可以自定义浏览器的右键菜单,主要是通过`chrome.contextMenus`API实现,右键菜单可以出现在不同的上下文,比如普通页面、选中的文字、图片、链接,等等,如果有同一个插件里面定义了多个菜单,Chrome会自动组合放到以插件名字命名的二级菜单里,如下: ![](https://img.kancloud.cn/27/d0/27d00fc14a94e6c202a76419983af16d_644x369.png) ### 最简单的右键菜单示例 ~~~ // manifest.json {"permissions": ["contextMenus"]} // background.js chrome.contextMenus.create({ title: "测试右键菜单", onclick: function(){alert('您点击了右键菜单!');} }); ~~~ ![](https://img.kancloud.cn/2e/e4/2ee4352ecb1d2a2b7df309f562ed4f94_348x328.png) ### 添加右键百度搜索 ~~~ // manifest.json {"permissions": ["contextMenus", "tabs"]} // background.js chrome.contextMenus.create({ title: '使用度娘搜索:%s', // %s表示选中的文字 contexts: ['selection'], // 只有当选中文字时才会出现此右键菜单 onclick: function(params) { // 注意不能使用location.href,因为location是属于background的window对象 chrome.tabs.create({url: 'https://www.baidu.com/s?ie=utf-8&wd=' + encodeURI(params.selectionText)}); } }); ~~~ ![](https://img.kancloud.cn/a5/b4/a5b48cc52345d805fc4b89dfd99a9635_450x218.png) **[contextMenus语法说明](https://developer.chrome.com/extensions/contextMenus)** ``` chrome.contextMenus.create({ type: 'normal', // 类型,可选:["normal", "checkbox", "radio", "separator"],默认 normal title: '菜单的名字', // 显示的文字,除非为“separator”类型否则此参数必需,如果类型为“selection”,可以使用%s显示选定的文本 contexts: ['page'], // 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"],默认page onclick: function(){}, // 单击时触发的方法 parentId: 1, // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单 documentUrlPatterns: 'https://*.baidu.com/*' // 只在某些页面显示此右键菜单 }); // 删除某一个菜单项 chrome.contextMenus.remove(menuItemId); // 删除所有自定义右键菜单 chrome.contextMenus.removeAll(); // 更新某一个菜单项 chrome.contextMenus.update(menuItemId, updateProperties); ``` * ## **<span style="color:red">options 页面</span>** Google Chrome扩展可以有一个options 页面,支持用户定制Chrome扩展的运行参数。 * ## **<span style="color:red">override(覆盖特定页面)</span>** Google Chrome扩展中的override页面可以替换浏览器中打开的默认页面,如标签管理器页面chrome://bookmarks、浏览历史记录页面chrome://history或新建Tab页面chrome://newtab。不过,一个Google Chrome扩展只能替换一个默认页面。 代码(注意,一个插件只能替代一个默认页,以下仅为演示): ~~~ "chrome_url_overrides": { "newtab": "newtab.html", "history": "history.html", "bookmarks": "bookmarks.html" } ~~~ * 通过**chrome.tabs.create()** 或**window.open()** 打开的页面 出了上面几种可见的展示形式还有下面三种展示形式 * ## **<span style="color:red">[devtools 开发控制台](https://developer.chrome.com/extensions/devtools)</span>** >[info]自定义一个和多个和`Elements`、`Console`、`Sources`等同级别的面板; 自定义侧边栏(sidebar),目前只能自定义`Elements`面板的侧边栏; devtools页面的生命周期和devtools窗口是一致的(F12窗口关闭,页面也随着关闭)。devtools页面可以访问一组特有的`DevTools API`以及有限的扩展API,这组特有的`DevTools API`只有devtools页面才可以访问,background都无权访问,这些API包括: * `chrome.devtools.panels`:面板相关; * `chrome.devtools.inspectedWindow`:获取被审查窗口的有关信息; * `chrome.devtools.network`:获取有关网络请求的信息; 大部分扩展API都无法直接被`DevTools`页面调用,但它可以像`content-script`一样直接调用`chrome.extension`和`chrome.runtime`API,同时它也可以像`content-script`一样使用Message交互的方式与background页面进行通信。 ### **实例:创建一个devtools扩展** 首先,要针对开发者工具开发插件,需要在清单文件声明如下: ~~~ { // 只能指向一个HTML文件,不能是JS文件 "devtools_page": "devtools.html" } ~~~ 这个`devtools.html`里面一般什么都没有,就引入一个js: ~~~ <!DOCTYPE html> <html> <head></head> <body> <script type="text/javascript" src="js/devtools.js"></script> </body> </html> ~~~ 可以看出来,其实真正代码是`devtools.js`,html文件是“多余”的,所以这里觉得有点坑,`devtools_page`干嘛不允许直接指定JS呢? 再来看devtools.js的代码: ~~~ // 创建自定义面板,同一个插件可以创建多个自定义面板 // 在`Panel.html`添加一个面板,几个参数依次为:panel标题、图标(其实设置了也没地方显示)、要加载的页面、加载成功后的回调 chrome.devtools.panels.create('MyPanel','icons/3601643092096124127.PNG','theme/mypanel.html', function(panel) { console.log('自定义面板创建成功!'); // 注意这个log一般看不到 }); // 创建一个自定义侧边栏 chrome.devtools.panels.elements.createSidebarPane("Images", function(sidebar) { // sidebar.setPage('../sidebar.html'); // 指定加载某个页面 sidebar.setExpression('document.querySelectorAll("img")', 'All Images'); // 通过表达式来指定 //sidebar.setObject({aaa: 111, bbb: 'Hello World!'}); // 直接设置显示某个对象 }); ~~~ mypanel.html ``` <!DOCTYPE html> <html> <head></head> <body> <div>这是一个自定义面板</div> </body> </html> ``` setPage时的效果: ![](https://img.kancloud.cn/3a/77/3a777116455c46161d1a2b3c896cb42e_899x223.png) ![](https://img.kancloud.cn/be/be/bebe6e5f0037dc6a57807d772085a10a_1692x241.png) ## **更近一步** **mypanel.html** ``` <!DOCTYPE html> <html> <head> <title>新标签页</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style> html,body{height: 100%;} body{font-family: 'Microsoft Yahei';margin:0;padding:0;} .center { position: fixed; left: 0; top: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 40px; color: #CCC; } .content { position: absolute; left: 20px; top: 10px; } </style> </head> <body> <div class="center"><p>这是一个自定义Chrome开发者工具页面</p></div> <div class="content"> <div><a href="javascript:;" id="check_jquery">检测当前页面jQuery</a></div> <div><a href="javascript:;" id="open_resource">查看当前页面HTML代码的第20行</a></div> <div><a href="javascript:;" id="test_inspect">审查当前页面第一张图片</a></div> <div><a href="javascript:;" id="get_all_resources">获取当前页面所有Resources</a></div> </div> <script type="text/javascript" src="js/mypanel.js"></script> </body> </html> ``` **mypanel.js** ``` // 检测jQuery document.getElementById('check_jquery').addEventListener('click', function() { // 访问被检查的页面DOM需要使用inspectedWindow // 简单例子:检测被检查页面是否使用了jQuery chrome.devtools.inspectedWindow.eval("jQuery.fn.jquery", function(result, isException) { var html = ''; if (isException) html = '当前页面没有使用jQuery。'; else html = '当前页面使用了jQuery,版本为:'+result; alert(html); }); }); // 打开某个资源 document.getElementById('open_resource').addEventListener('click', function() { chrome.devtools.inspectedWindow.eval("window.location.href", function(result, isException) { chrome.devtools.panels.openResource(result, 20, function() { console.log('资源打开成功!'); }); }); }); // 审查元素 document.getElementById('test_inspect').addEventListener('click', function() { chrome.devtools.inspectedWindow.eval("inspect(document.images[0])", function(result, isException){}); }); // 获取所有资源 document.getElementById('get_all_resources').addEventListener('click', function() { chrome.devtools.inspectedWindow.getResources(function(resources) { alert(JSON.stringify(resources)); }); }); ``` ![](https://img.kancloud.cn/5a/87/5a871dad3402263bae9fef93fc69c474_244x129.png) ### 调试技巧 修改了devtools页面的代码时,需要先在[chrome://extensions](chrome://extensions/)页面按下`Ctrl+R`重新加载插件,然后关闭再打开开发者工具即可,无需刷新页面(而且只刷新页面不刷新开发者工具的话是不会生效的)。 由于devtools本身就是开发者工具页面,所以几乎没有方法可以直接调试它,直接用`chrome-extension://extid/devtools.html"`的方式打开页面肯定报错,因为不支持相关特殊API,只能先自己写一些方法屏蔽这些错误,调试通了再放开。 * ## **omnibox** `omnibox`是向用户提供搜索建议的一种方式 首先,配置文件如下: ~~~ { // 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字 "omnibox": { "keyword" : "go" }, } ~~~ 然后`background.js`中注册监听事件: ~~~ // omnibox 演示 chrome.omnibox.onInputChanged.addListener((text, suggest) => { console.log('inputChanged: ' + text); if(!text) return; if(text == '美女') { suggest([ {content: '中国' + text, description: '你要找“中国美女”吗?'}, {content: '日本' + text, description: '你要找“日本美女”吗?'}, {content: '泰国' + text, description: '你要找“泰国美女或人妖”吗?'}, {content: '韩国' + text, description: '你要找“韩国美女”吗?'} ]); } else if(text == '微博') { suggest([ {content: '新浪' + text, description: '新浪' + text}, {content: '腾讯' + text, description: '腾讯' + text}, {content: '搜狐' + text, description: '搜索' + text}, ]); } else { suggest([ {content: '百度搜索 ' + text, description: '百度搜索 ' + text}, {content: '谷歌搜索 ' + text, description: '谷歌搜索 ' + text}, ]); } }); // 当用户接收关键字建议时触发 chrome.omnibox.onInputEntered.addListener((text) => { console.log('inputEntered: ' + text); if(!text) return; var href = ''; if(text.endsWith('美女')) href = 'http://image.baidu.com/search/index?tn=baiduimage&ie=utf-8&word=' + text; else if(text.startsWith('百度搜索')) href = 'https://www.baidu.com/s?ie=UTF-8&wd=' + text.replace('百度搜索 ', ''); else if(text.startsWith('谷歌搜索')) href = 'https://www.google.com.tw/search?q=' + text.replace('谷歌搜索 ', ''); else href = 'https://www.baidu.com/s?ie=UTF-8&wd=' + text; openUrlCurrentTab(href); }); // 获取当前选项卡ID function getCurrentTabId(callback) { chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { if(callback) callback(tabs.length ? tabs[0].id: null); }); } // 当前标签打开某个链接 function openUrlCurrentTab(url) { getCurrentTabId(tabId => { chrome.tabs.update(tabId, {url: url}); }) } ~~~ ![](https://img.kancloud.cn/e4/1d/e41dbc92e72aae64b2094174e267aebf_463x350.gif) * ## **notification** Chrome提供了一个`chrome.notifications`API以便插件推送桌面通知,暂未找到`chrome.notifications`和HTML5自带的`Notification`的显著区别及优势。 在后台JS中,无论是使用`chrome.notifications`还是`Notification`都不需要申请权限(HTML5方式需要申请权限),直接使用即可。 代码: ~~~ chrome.notifications.create(null, { type: 'basic', iconUrl: 'img/icon.png', title: '这是标题', message: '您刚才点击了自定义右键菜单!' }); ~~~