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