chrome

Chrome扩展和Web Page 进行通讯

由于 WEB 页面本身受到浏览器的安全策略限制,而且 Content Scripts 和页面本身并不共享环境变量.我们很多时候都是 通过 DOM 去注入 JS 代码到当前页面上.因为同源策略的原因不得不把有些操作发回到 Background 进行处理,因为 Background 没有任何限制,可以拥有完整的 Chrome 的 API 权限.

# 通过 Content Scripts 中转

第一种通讯方式是通过 Web API postMessage (opens new window) 发送消息,然后在 Content Scripts 监听 message 事件捕捉消息发送回 Background 这种方式适合单向通讯,只从 web page 发送数据到 Background 或者 Background 发送给 web page 如果双向通讯的话就很不方便了 因为 Content Scripts 和 web page 共享 window 会造成事件重复绑定的.

# 使用 Chrome API

首先在 manifest.json 里声明权限

"externally_connectable": {
  "matches": ["*://*.example.com/*"]
}
1
2
3

声明要通讯的网页的网址,然后和 Content Script 一样建立通讯

var port = chrome.runtime.connect(chrome_id, { name: 'abc' });
port.postMessage({
  method: 'rpc_data',
  data: parameter
});
port.onMessage.addListener(function(response) {
  SetMessage(response[0], response[1]);
});
1
2
3
4
5
6
7
8

关键是 Background 监听消息的接收,必须使用 chrome.runtime.onConnectExternal.addListener 而不是 chrome.runtime.onConnect.addListener 后者是接收 Content Script 消息的,前者是 接收 Web Page 消息的. 这样便实现了 Web Page 和 Background 之间的双向通讯, Web Page 不能完成的事情交给 Background 来处理即可.

# 参考

官方文档 (opens new window)

Chromebook Pixel (2013) 安装Linux的方法

同学最近淘了一台二手 Chromebook Pixel (2013),这款笔记本最大的诱惑就是 12.85 寸但是配备了一块 2560 x 1700 分辨率的触摸屏。上网的体验不要太爽啊,在体验了一小时的 Chrome OS 之后,便感叹就只有一个浏览器而已啊!于是开始折腾如何安装 Linux,毕竟这款笔记本本来就对 Linux 支持很好。下面开始介绍安装过程。

由于这款笔记本是 x86 架构还自带 SeaBIOS,所以在安装 Linux 上省了不少功夫,很多非 SeaBIOS 的 Chromebook 都是需要刷固件才能引导 Linux 的。

# 启用开发者模式

  • 启动电脑
  • 按住 Esc 和 F3(刷新)按钮,这时候按电源键,即可进入 Recovery 模式
  • 按 Ctrl+ D,会提示你是否要进入开发者模式,确认之后等一会系统就会转变成启用开发者模式

这时候每次开机屏幕都有警告提示,要么按 Ctrl + D,或者等待 30s 系统响一声便进入系统。

# 进入超级用户 shell

  • 按 Ctrl+Alt+F2(→),之后你会看到一个登录提示
  • 输入 chronos 作为用户名,没有密码
  • 然后输入 sudo bash,以超级用户使用 shell

# 启用 SeaBIOS

在终端里面输入下面的指令:

# crossystem dev_boot_usb=1 dev_boot_legacy=1
1

之后重启电脑即可。之后每次进入 SeaBIOS 都要在开机的时候按 Ctrl + L

# 将 SeaBIOS 设置成默认启动

首先你需要破解硬件写保护,按照 wiki 上的说法需要拆机和跳线,具体的操作可以查看这个wiki (opens new window),拆机操作非常危险,请自己慎重考虑。

如果你已经成功破解硬件写保护,接下来就是解除软件写保护。同样是进入超级用户 shell 然后输入指令禁用软件写保护:

# flashrom --wp-disable
1

检查软件写保护:

# flashrom --wp-status
1

运行 set_gbb_flags.sh :

# set_gbb_flags.sh
1

注意:新版本的 Chrome OS 已经把这个脚本移动到 /usr/share/vboot/bin/set_gbb_flags.sh位置了,然而并不在$PATH 环境变量中。

确认有下面的输出:

GBB_FLAG_DEV_SCREEN_SHORT_DELAY 0x00000001
GBB_FLAG_FORCE_DEV_SWITCH_ON 0x00000008
GBB_FLAG_FORCE_DEV_BOOT_LEGACY 0x00000080
GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY 0x00000400
1
2
3
4

现在可以设置 SeaBIOS 为默认了:

# set_gbb_flags.sh 0x489
1

最后再重新启用写保护:

# flashrom --wp-enable
1

现在可以使用 Linux 安装 U 盘进行安装了。

# 写在最后

插上 U 盘,开机然后按 Ctrl+ L 发现启动光盘正常启动,然后选择安装之后突然提示。。。

not enough memory to load specified image
1

这时候不要慌张,在安装选择菜单点击 Tab 键,可以编辑启动命令,在双横线前面输入 mem=4G 注意是双横线前面!

恢复 Chrome OS,请参阅Recover your Chromebook (opens new window)

# 参考

Chrome OS devices (opens new window)

Chromebook (opens new window)

Installing Linux on the Chromebook Pixel (opens new window)

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 是去掉前后的注释符.