續上一篇文章
直接在網站頁面上安裝你的 Chrome extension
最後提到為了能夠在提供安裝的頁面展現出我們的誠意(明明上一篇好像不是這樣說的),總得有個辦法來各別針對 未安裝/已安裝 的使用者做頁面的客製化。
基本原理
其實就是官方文件提供的方法。
Extensions can communicate with the embedding page via content scripts to let it know that they are already installed.
可以歸納出大致兩種基本作法:
- 在需要檢查的頁面讀入特定 content script 之後,檢查時利用
window.postMessage
與 content script 對話,基本上只要約定一下傳輸的內容就可以知道 extension 安裝成功 - 在需要檢查的頁面讀入特定 content script,讓它直接在該頁面上塞一個
<div id="my-awesome-extension"></div>
,檢查的動作只要檢查該div
是否存在即可
實作
這邊選擇第二種做法,節奏明快!
content script
首先準備好要跑的 content script,取名為 installed.js
。
/* file: installed.js */
var INSTALLED_NODE_ID = 'my-awesome-extension';
// 檢查 div 是否已存在
var getNode = function () {
return document.getElementById(INSTALLED_NODE_ID);
};
// 擺上 div
var insertNode = function () {
var isInstalledNode = document.createElement('div');
isInstalledNode.id = INSTALLED_NODE_ID;
isInstalledNode.style.visibility = 'hidden'
document.body.appendChild(isInstalledNode);
};
if (!getNode()) {
insertNode();
}
修改 manifest.json 以載入 installed.js
最標準的插入 content script 方法就是在 manifest.json 裡加上一筆!
/* file: manifest.json */
{
"name": "威力彩上看18億",
...
"content_scripts": [
{
"matches": ["*://你的網域/*"],
"js": ["installed.js"]
}
],
...
}
Progammatically inject installed.js
Programmatic injection(以下簡稱 PI) 在這個情況下,跟修改 manifest.json 相比真是吃力又不討好,偏偏本人的第一個 extension 就直接走上了 PI 之路…
也因此,原本我的 manifest.json 裡是沒有擺 "content_scripts"
這項的 XD
於是原本我也很自然的選用了 PI installed.js
,學個經驗,還是來看一下 PI 做法。
首先是需要知道 PI 的時機 ─ 當瀏覽到需要知道 extension 是否已安裝的頁面的時候(對應上面的 manifest.json 就是 *://你的網域/*
),這邊可以透過一個官方建議的方法來做到,它是一個使用在 event page 的小 trick(所以應該也可以在一般的 background page 使用,我的 extension 本身是採用 event page)。
chrome.webNavigation
API 需要在 manifest.json 裡要求 webNavigation
權限。
/* file: background.js */
var urlFilter = {url: [{hostSuffix: '你的網域'}]};
// onCommitted, onDOMContentLoaded inject 的 timing 似乎差不多,有點神秘
chrome.webNavigation.onCommitted.addListener(function (detail) {
chrome.tabs.executeScript(detail.tabId, 'installed.js');
}, urlFilter);
然而到這邊還不算結束,很遺憾的 PI 的動作相較於 manifest.json 的載入 content script 似乎就是慢了一些。
如果你的頁面不是在使用者進行了一些操作時才做檢查,而是在初始化的階段(比如說 DOMContentLoaded 前後),就先做檢查的動作的話,你可能會需要稍微將檢查 delay 個 100ms 左右。
我在 AngularJS 裡寫了一個 service 來做檢查,它會被當頁的 controller 呼叫而做檢查的動作,而除了 delay 過的位置之外,得到的結果都是 false
。
/* file: is-extension-installed.js */
app
.factory('$isExtensionInstalled', [
'$document', '$timeout', '$log',
function ($document, $timeout, $log) {
var INSTALLED_NODE_ID = 'my-awesome-extension',
CHECK_DELAY = 100;
$log.debug('init check', ($document[0].getElementById(INSTALLED_NODE_ID)) ? true : false);
return function $isExtensionInstalled(callback) {
$log.debug('function call check', ($document[0].getElementById(INSTALLED_NODE_ID)) ? true : false);
$timeout(function () {
$log.debug('delayed check', ($document[0].getElementById(INSTALLED_NODE_ID)) ? true : false);
var isInstalled = ($document[0].getElementById(INSTALLED_NODE_ID)) ? true : false;
(callback || angular.noop)(isInstalled);
}, CHECK_DELAY);
};
}]);