Thinking_Out_Loud

写一个Chrome扩展

2018-10-16

伪需求

在公司里想要从外网电脑往内网电脑搬运资料和一些代码只能通过内部的IM里的“文件助手”中转,大大降低了面向搜索引擎编程的生产效率。所以亟需一个在浏览器右键菜单就可以直接发送选中文本等的Chrome插件。

分析一下问题

好在IM有Web端,我直接写一个扩展使用Chrome的API就可以操作数据了。初步设想是在页面内选中文本后,右键呼出菜单,菜单里有扩展的选项用IM里的接口将选中文本POST到“文件助手”的聊天记录里,然后信息就可以同步到内网机上了。

接着打开Chrome的控制台,往“文件助手”发几条信息,观察一下哪些接口是发送信息需要的。非常幸运的是,只有一个sendMessage.do是需要调用的,请求参数有groupIDtoUserId,前者应该是标识一个会话,后者则是“文件助手”的uid了,余下的还有一些消息内容相关的参数。至于鉴权用的token则都放在了cookie里。另外,在测试这个接口的时候,我发现只要把groupId改为其他人的就可以乱入其他人的“文件助手”聊天窗口,不知道这算BUG还是feature,XD。

Chrome.< API >

其实跟着官方的教程走一遍就可以知道扩展的开发方式,主要是对manifest.json文件的配置还有Chrome API的调用。

首先,根据需求,我们需要操作右键菜单和发送网络请求,容易找到:

chrome.contextMenus

这就是操作右键菜单的API,大概流程就是在扩展安装时配置菜单项,点击事件。

如下就配置好了一个右键菜单项:

1
2
3
4
5
6
chrome.contextMenus.create({
title: 'Send "%s" to G', // '%s' 是已选中文本的占位符
type: 'normal',
id: SELECTION_MENU_ID,
contexts: ['selection'] // 在选中文本时出现,'image'则是在右击图像元素是出现
});

如下配置事件回调,后期增加了一个发送图片的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
chrome.contextMenus.onClicked.addListener(function (info) {

switch (info.menuItemId) {
case SELECTION_MENU_ID:
handleTextSelected(info);
break;
case IMAGE_MENU_ID:
handleImgSelected(info);
break;
default:
console.log("Unexpected contextmenu ID");
break;
}

});

chrome.storage

因为每个人的groupId都不同,不能硬编码到代码中,我们可以使用chrome.storage存储到本地。

跨域XMLHttpRequest

发送网络请求遇到跨域问题时可以在manifest.json申请这个网站的权限来让扩展规避同源限制,也可以使用通配符的方式申请所有网站的权限,注意不能漏掉结尾的/

1
2
3
4
5
6
7
8
{
"name": "My extension",
...
"permissions": [
"*://*/"
],
...
}

回调地狱

在之后又增加了发送图片的功能,但是发送图片居然先后调用了4个接口才完成操作,3个以上的回调就是一个回调地狱了啊!不过好在可以用Promise包装调用,包装后如下:

1
2
3
4
5
6
7
8
9
10
11
12
downloadImg(srcUrl)
.then((imgBlob) => {
let fileName = srcUrl.substr(srcUrl.lastIndexOf('/') + 1).split('?')[0];
return { 'fileName': fileName, 'imgBlob': imgBlob }
})
.then(uploadImg)
.then(getFileInfo)
.then((res)=>{
return res.fileInfo;
})
.then(sendImgMsg)
.then(saveFile);

后话

打算再加上一个将页面作为PDF发送的功能,不过这可能又是一个TODO。