chrome扩展开发经验记录

最近写了两个 chrome 扩展.看了不少 chrome 扩展开发的文档.觉得还是写点东西比较好.

# Background Pages

一个扩展肯定需要长时间运行的脚本对扩展进行管理.这个脚本就属于 Background Pages 的一部分. 不过由于 Background Pages 是一直运行的,对资源占用比较多.现在已经用 Event Pages 代替.Event Pages 是按需加载,不需要的时候不会激活运行. Event Pages 的 JS 可以使用 chrome 的所有 API.

官方文档 (opens new window)

# Content Scripts

Content Scripts 是运行在网页上的. manifest 上进行网址匹配.当是目标网址的时候就加载这个 JS. Content Scripts 可以获取到匹配网址的 DOM,并进行修改.Content Scripts 也可以使用 chrome 的 API,但是只能使用比较有限的 API.

官方文档 (opens new window)

# Message Passing

这个是 chrome 的通讯机制,是非常重要的一个知识点.开发扩展的 content scripts 几乎都是需要和 background pages 进行通讯的. 因为 content scripts 可以直接操作 DOM,background pages 可以使用所有的 chromeAPI.所以这俩 JS 肯定要互通有无.接下来重点讲解 chrome 的通讯机制.

官方文档 (opens new window)

# Simple one-time requests

官方文档有例子.但是我要说的是这个 Simple requests 真的太 Simple. content js 使用 sendMessage,就立即回调获取 response. 如果 background page 稍微进行处理下占用点时间就会导致 response 的函数根本无法执行的情况. 推测可能是没接收到数据就直接 pass 了.所以稍微复杂点需要处理数据的通讯操作千万不能用这个.

# Long-lived connections

这个通讯方式也是我比较推荐的通讯方式,生命周期长.并且通讯效果好,可以多次通讯一次监听. 官网给出的 content js 例子虽然是先 postMessage 之后再 addListener.但是强烈推荐先 监听之后再发送数据.因为当你发送数据之后,background 可能会立马给你返回数据.这时候可能会造成 没有监听到的情况... 之后的通讯方式我还没用到就不说了.

# 网页 JS 和 content js 通讯

网页 JS 也有自己的局限性,例如无法获取到 http only 的 cookies.这时候可以通过 content js 的帮助. content js 获取到数据之后怎么发给网页 JS 呢? content js 使用 window.postMessage 进行发送数据:

window.postMessage(msg, '*');
1

网页 JS 通过监听事件进行捕捉数据:

window.addEventListener('message', receiveMessage, false);

function receiveMessage(event) {
  if (event.origin == window.location.origin) {
    console.log(event.data);
  }
}
1
2
3
4
5
6
7

注意 content js 和网页 JS 不共享变量,所以不能通过全局变量的方式进行通讯.不过可以通过 DOM 进行通讯.

# content js 使用网页 JS 的变量.

如果网页本身加载了很多组件例如 JQuery,自己想使用但是因为不共享变量导致无法使用.可以使用 append 的 方式把自己 content js 里面写的函数直接注入到网页的 DOM 中,因为 DOM 是共享的.这样 content js 写的函数 就变成网页的 JS 进行运行了~

# content js 注入

var script = document.createElement('script');
script.id = 'baidu_script';
script.appendChild(document.createTextNode('(' + baidu + ')();'));
(document.body || document.head || document.documentElement).appendChild(
  script
);
1
2
3
4
5
6

这里注入的是一个立即执行函数,append 到 DOM 上的时候就会立即执行,并且可以使用网页 JS 的变量.

# css 注入

有时候修改 DOM 的话肯定需要更改样式啊,这时候用内联样式必然太没效率,不能重用.就需要添加 CSS.

var css = function() {
  /*
  input{
  border: 1px solid #C6C6C6;
  box-shadow: 0 0 3px #C6C6C6;
  -webkit-box-shadow: 0 0 3px #C6C6C6;
  }
  */
}
  .toString()
  .slice(15, -4);
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.textContent = css;
document.head.appendChild(style);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

首先定义一个匿名函数赋值给 css 变量,然后里面写了 css 内容,由于是注释掉的,其实并不会被 JS 执行,最后 转换成字符串的时候是能读到 CSS 文本的,slice 是去掉前后的注释符.

Linux下恢复误删除的文件

经常有时候喜欢删除文件...但是之后又后悔删除... Linux 下 rm 之后是没有回收站的.
于是我便寻找恢复文件的相关资料.

# TestDisk

TestDisk 是用来帮助恢复丢失的分区和使无法启动的启动盘再次启动.
使用 TestDisk 之前别忘了备份分区表.

# 备份分区表

# sfdisk -d /dev/sda > /tmp/sda.bak
1

# 恢复分区表

# sfdisk /dev/sda < /tmp/sda.bak
1

官方教程:TestDisk Step By Step (opens new window)

# PhotoRec

PhotoRec 是用来恢复丢失的文件和照片.即使重新格式化或严重损坏的文件系统都可以恢复. 但是 PhotoRec 只有全盘扫描和空闲分区扫描.耗时很长.对于自己无意中删除的小文件来说就是 杀鸡焉用牛刀.没有这个必要.

# Extundelete

Extundelete 设计的就是用来从 ext3 或 ext4 的分区恢复最近被删除的文件.它可以从一个相对路径恢复 被删除的文件,非常实用.但是只有当分区被卸载才可以使用.

# 恢复被删除的文件

# extundelete --restore-file tux/cv.tex /dev/sda4
1

# 恢复被删除的文件目录

# extundelete --restore-directory tux/Documents/tex/ /dev/sda4
1

Arch wiki:File recovery (opens new window)

百度网盘新版API

突然发现百度网盘又更新 API 了....明明 UI 一点都没有更新.我严重怀疑这个前端工程师是有多寂寞... 没事就更新 JS...这已经是我观察发现的第三次更新了...每次都导致我的 aria2c 导出脚本失效...

于是我又苦苦寻找新版的 JS 到底是怎么实现下载的,虽说可以直接操纵 DOM 获取选中的文件的 fs_id 信息 然后 POST 数据获取 dlink(下载地址),但是这样怎么也不够优雅啊...因为百度自己的 JS 肯定实现了这部分内容 于是我便花了更多的时间找 API,但是百度那既压缩又混淆的 JS 代码真是读起来特带劲啊(我绝对不是抖 M)

于是说一下百度的新版 API 吧:

# Toast 模块

新版 API 把每个功能全部模块化,声明某个模块的方法是使用 require 方法.

var SetMessage = function(msg, type) {
  var Toast = require('common:widget/toast/toast.js');
  Toast.obtain.useToast({
    toastMode: Toast.obtain[type],
    msg: msg,
    sticky: false
  });
};
1
2
3
4
5
6
7
8

于是我便封装了一个函数用来实现消息提示.

# data-center 模块

var File = require('common:widget/data-center/data-center.js');
return File.get('selectedList');
1
2

data-center 模块可以获取选择的文件的 fs_id,通过传入 fs_id 可以获取 dlink. File.get("selectedItemList")可以获取选择的文件的 DOM 结构.

# commonService 模块

var Service = require('common:widget/commonService/commonService.js');
Service.getDlink(
  JSON.stringify(File.get('selectedList')),
  'dlink',
  self.aria2_rpc.bind(self)
);
1
2
3
4
5
6

commonService 模块有很多实用的功能,但是我们目前只需要这么一个就足够了,有需要的可以自己研究下.getDlink 接受三个参数.第一个是 每个要下载的文件的 fs_id,多个文件是数组的字符串方式传入,第二个是获取 dlink 的类型,总共有三个类型 batsh,dlink,nolimit,第一个类型 是把传入的多个文件合并成一个压缩包进行下载,返回一个 dlink,第二个是单独获取每个文件的 dlink,第三个还没有尝试过.第三个参数是回调函数 会传入 POST 之后获取的 json 数据.

# 设置 UA

因为我现在还没找到怎么获取选择的文件名的列表,所以在 aria2c 的时候无法手动指定文件名.貌似不写 UA 的情况下会被限速. 设置 chrome 的 UA 文件名会被 encode,所以 aria2c 的时候设置 UA 为百度云管家的 UA

var UA = 'netdisk;4.4.0.6;PC;PC-Windows;6.2.9200;WindowsBaiduYunGuanJia';
1

百度云盘升级之后仅仅只有 dlink 还不行,还需要一个 name 为BDUSS的 cookie. 但是这个 cookie 被设置为 http only,所以 JS 是无论如何也获取不到的.目前只能采用手动设置的方法. 还好这个 cookie 每个用户基本都是唯一的,设置一次永久使用,否则真是麻烦.

完整版实现GitHub (opens new window)