アドオン開発について
今回はAdmatrix AnalyticsというChromeとFirefoxに関連した拡張機能・アドオンを採り上げます。SEOアナライザのような拡張機能で、サイトのSEOの内部対策を行っていくために必要な情報を抽出するアドオンです。
アドオンのスクリプトは操作すれば誰でも見られるようになっていますので、ここで一挙に公開いたします。
Google Chrome側の開発
Google Chromeで開発した拡張機能用のJavaScripyは以下のようになっています。
var page_mod = require("sdk/page-mod");
const {Cc, Ci} = require('chrome');
var request = require("sdk/request");
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var data = require("sdk/self").data;
var self = require("sdk/self");
var ss = require("sdk/simple-storage").storage;
ss['AdmatrixStatus'] = 'open';
var id = '0';
var mediator = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
// exports.main is called when extension is installed or re-enabled
exports.main = function(options, callbacks) {
addToolbarButton();
};
// exports.onUnload is called when Firefox starts and when the extension is disabled or uninstalled
exports.onUnload = function(reason) {
removeToolbarButton();
};
// add our button
function addToolbarButton() {
// this document is an XUL document
var document = mediator.getMostRecentWindow('navigator:browser').document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
if (!navBar) {
return;
}
var popup = require("sdk/panel").Panel({
width: 180,
height: 120,
position: {top:0,right:0},
contentURL: data.url("panel.html"),
contentScriptFile: [data.url("jquery.js"), data.url("popup.js")]
});
popup.on("show", function() {
if(ss['AdmatrixStatus']){
status = ss['AdmatrixStatus'];
};
popup.port.emit("show",status);
});
popup.port.on("toggle", function () {
status = ss['AdmatrixStatus'];
worker = tabs.activeTab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
if(status == 'close'){
// close -> open
worker.port.emit("inject",id);
ss['AdmatrixStatus'] = 'open';
popup.port.emit("btn",'close');
btn.setAttribute('image', data.url('images/icon/19.png'));
}else{
// open -> close
worker.port.emit("remove");
ss['AdmatrixStatus'] = 'close';
popup.port.emit("btn",'open');
btn.setAttribute('image', data.url('images/icon/19_off.png'));
}
});
btn.setAttribute('id', 'mybutton-id');
btn.setAttribute('type', 'button');
// the toolbarbutton-1 class makes it look like a traditional button
btn.setAttribute('class', 'toolbarbutton-1');
// the data.url is relative to the data folder
btn.setAttribute('image', data.url('images/icon/19.png'));
btn.setAttribute('orient', 'horizontal');
// this text will be shown when the toolbar is set to text or text and iconss
btn.setAttribute('label', 'My Button');
btn.addEventListener('click', function() {
popup.show();
status = ss['AdmatrixStatus'];
worker = tabs.activeTab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
//popup.port.emit("show",status);
if(status == 'close'){
worker.port.emit("inject",id);
ss['AdmatrixStatus'] = 'open';
btn.setAttribute('image', data.url('images/icon/19.png'));
}else{
worker.port.emit("remove");
ss['AdmatrixStatus'] = 'close';
btn.setAttribute('image', data.url('images/icon/19_off.png'));
}
// do stuff, for example with tabs or pageMod
}, false)
navBar.appendChild(btn);
}
function removeToolbarButton() {
var enumerator = mediator.getEnumerator("navigator:browser");
while(enumerator.hasMoreElements()) {
var document = enumerator.getNext().document;
var navBar = document.getElementById('nav-bar');
var btn = document.getElementById('mybutton-id');
if (navBar && btn) {
navBar.removeChild(btn);
}
}
}
initPageMod();
function initPageMod()
{
myPageMod = page_mod.PageMod({
include: ['*'],
contentScriptWhen: "end",
contentScriptFile: [data.url("jquery.js"), data.url("modPage.js")],
attachTo: ["top"],
onAttach: function(worker) {
if(ss['AdmatrixId']){
id = ss['AdmatrixId'];
}else{
}
if(ss['AdmatrixStatus'] == 'open'){
request.Request({
url : "https://seo-analytics.fs-site.net/1.0.0/"+ id +"/createUser.php",
content:{
loaded_url:worker.url
},
onComplete : function(response){
id = JSON.parse(response.text).id;
ss['AdmatrixId'] = id;
worker.port.emit("inject",id);
}
}).get();
}
}
});
}
Firefoxのアドオン開発
var page_mod = require("sdk/page-mod");
const {Cc, Ci} = require('chrome');
var request = require("sdk/request");
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var data = require("sdk/self").data;
var self = require("sdk/self");
var ss = require("sdk/simple-storage").storage;
var prefs = require('sdk/simple-prefs');
var mediator = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
// exports.main is called when extension is installed or re-enabled
exports.main = function(options, callbacks) {
addToolbarButton();
initPageMod();
makeOptions();
};
// exports.onUnload is called when Firefox starts and when the extension is disabled or uninstalled
exports.onUnload = function(reason) {
removeToolbarButton();
};
function getExceptions(url) {
if(!exceptions_enable) {
return false;
}
var domainLabels = url.split('/')[2].split(':')[0].split('.').reverse();
var loadedExceptions = ss['exceptions'];
loadedExceptions = (loadedExceptions == null) ? [] : loadedExceptions.split(/\t/);
var len = loadedExceptions.length;
for (var i=0; i<len; i++) {
var http = loadedExceptions[i].indexOf('http://');
var https = loadedExceptions[i].indexOf('https://');
var ftp = loadedExceptions[i].indexOf('ftp://');
if(http == 0 || https == 0 || ftp == 0) {
if(loadedExceptions[i] == url) {
//console.log(true);
return true;
}
} else {
var same = false;
var exceptionLabels = loadedExceptions[i].split('.').reverse();
var len2 = exceptionLabels.length;
var len3 = (exceptionLabels[len2 - 1] == '*') ? len2 - 1 : len2;
for (var p=0; p<len3; p++) {
//console.log(exceptionLabels[p]+':'+domainLabels[p]);
if(exceptionLabels[p] == domainLabels[p]) {
same = true;
} else {
same = false;
break;
}
}
if(exceptionLabels[len2 - 1] == '*') {
if(same == true) {
//console.log(true);
return true;
}
} else {
if(domainLabels.length != len2) {
same = false;
}
}
if(same) {
//console.log(true);
return true;
}
}
}
//console.log(false);
return false;
}
function versionCheck() {
if(ss['AdmatrixId'] != null && ss['addon-version'] != self.version) {//2回目以降の起動でアップデートが検知された場合
ss['AdmatrixStatus'] = 'open';
ss['addon-version'] = self.version;
return true;
} else {
if(ss['AdmatrixId'] == null) {//インストール後、初回の起動の場合
ss['AdmatrixStatus'] = 'open';
ss['addon-version'] = self.version;
}
return false;
}
}
function makeOptions(){
page_mod.PageMod({
include: data.url("options.html"),
contentScriptFile: [data.url("jquery.js"), data.url("options.js")],
onAttach: function(worker) {
worker.port.on("load-exceptions-enable", function(){
if(ss['AdmatrixId']){
id = ss['AdmatrixId'];
} else {
id = 'idIsNotStored';
}
request.Request({
url : "https://seo-analytics.fs-site.net/1.0.0/"+ id +"/createUser.php",
onComplete : function(response){
exceptions_enable = JSON.parse(response.text)['exceptions-enable'];
worker.port.emit("return-exceptions-enable", exceptions_enable);
}
}).get();
});
worker.port.on("load-data", function(){
worker.port.emit("return-data", ss['exceptions']);
});
worker.port.on("save-data", function(data){
ss['exceptions'] = data;
});
worker.port.on("delete-data", function(){
delete ss['exceptions'];
});
}
});
prefs.on('adMatrixAnalyticsOptions', function(name) {
tabs.open(data.url("options.html"));
});
}
// add our button
function addToolbarButton() {
// this document is an XUL document
var document = mediator.getMostRecentWindow('navigator:browser').document;
var navBar = document.getElementById('nav-bar');
var btn = document.createElement('toolbarbutton');
if (!navBar) {
return;
}
var popup = require("sdk/panel").Panel({
width: 180,
height: 120,
position: {top:0,right:0},
contentURL: data.url("panel.html"),
contentScriptFile: [data.url("jquery.js"), data.url("popup.js")]
});
popup.on("show", function() {
if(ss['AdmatrixStatus']){
status = ss['AdmatrixStatus'];
};
popup.port.emit("show",status);
});
popup.port.on("toggle", function () {
status = ss['AdmatrixStatus'];
if(status == 'close'){
// close -> open
ss['AdmatrixStatus'] = 'open';
popup.port.emit("btn",'close');
btn.setAttribute('image', data.url('images/icon/19.png'));
for each (var tab in tabs) {
worker = tab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
worker.port.on('save-state', function(state) {
ss['state' + worker.tab.id] = state;
});
inject(worker);
}
}else{
// open -> close
ss['AdmatrixStatus'] = 'close';
popup.port.emit("btn",'open');
btn.setAttribute('image', data.url('images/icon/19_off.png'));
for each (var tab in tabs) {
worker = tab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
worker.port.emit("remove");
}
}
});
btn.setAttribute('id', 'mybutton-id');
btn.setAttribute('type', 'button');
// the toolbarbutton-1 class makes it look like a traditional button
btn.setAttribute('class', 'toolbarbutton-1');
// the data.url is relative to the data folder
if(ss['AdmatrixStatus'] == 'open' || ss['AdmatrixStatus'] == null){
btn.setAttribute('image', data.url('images/icon/19.png'));
} else {
btn.setAttribute('image', data.url('images/icon/19_off.png'));
}
//2回目以降の起動でアップデートが検知されたらアイコンをオンにする
if(ss['AdmatrixId'] != null && ss['addon-version'] != self.version) {
btn.setAttribute('image', data.url('images/icon/19.png'));
}
btn.setAttribute('orient', 'horizontal');
// this text will be shown when the toolbar is set to text or text and iconss
btn.setAttribute('label', 'My Button');
btn.addEventListener('click', function() {
popup.show();
status = ss['AdmatrixStatus'];
//popup.port.emit("show",status);
if(status == 'close'){
ss['AdmatrixStatus'] = 'open';
btn.setAttribute('image', data.url('images/icon/19.png'));
for each (var tab in tabs) {
worker = tab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
worker.port.on('save-state', function(state) {
ss['state' + worker.tab.id] = state;
});
inject(worker);
}
} else {
ss['AdmatrixStatus'] = 'close';
btn.setAttribute('image', data.url('images/icon/19_off.png'));
for each (var tab in tabs) {
worker = tab.attach({
contentScriptFile: [data.url('jquery.js'), data.url('modPage.js')]
});
worker.port.emit("remove");
}
}
// do stuff, for example with tabs or pageMod
}, false)
navBar.appendChild(btn);
}
function removeToolbarButton() {
var enumerator = mediator.getEnumerator("navigator:browser");
while(enumerator.hasMoreElements()) {
var document = enumerator.getNext().document;
var navBar = document.getElementById('nav-bar');
var btn = document.getElementById('mybutton-id');
if (navBar && btn) {
navBar.removeChild(btn);
}
}
}
function initPageMod()
{
myPageMod = page_mod.PageMod({
include: ['*'],
contentScriptWhen: "end",
contentScriptFile: [data.url("jquery.js"), data.url("modPage.js")],
attachTo: ["top"],
onAttach: function(worker) {
//各tabが閉じられたら該当するストレージを削除するイベントハンドラ
worker.tab.on("close", function() {
delete ss['state' + worker.tab.id];
});
//stateの保存用のイベントハンドラ
worker.port.on('save-state', function(state) {
ss['state' + worker.tab.id] = state;
})
//tabごとのストレージがなかったら初期値を設定する
if(ss['state' + worker.tab.id] == null) {
ss['state' + worker.tab.id] = {"height" : "380", "minimize" : "open", "title" : "on"};
}
update = versionCheck();
//アップデートが検知されたらアップデート用の設定にする
if(update) {
ss['state' + worker.tab.id] = {"height" : "380", "minimize" : "close", "title" : "on"};
}
if(ss['AdmatrixId']){
id = ss['AdmatrixId'];
} else {
id = 'idIsNotStored';
}
request.Request({
url : "https://seo-analytics.fs-site.net/1.0.0/"+ id +"/createUser.php",
onComplete : function(response){
ss['AdmatrixId'] = JSON.parse(response.text)['id'];
token = JSON.parse(response.text)['token'];
script_version = JSON.parse(response.text)['script-version'];
title_view_enable = JSON.parse(response.text)['title-view-enable'];
exceptions_enable = JSON.parse(response.text)['exceptions-enable'];
inject(worker);
}
}).get();
}
});
}
function inject(worker) {
if(ss['AdmatrixStatus'] == 'open' && !getExceptions(worker.url)){
worker.port.emit("inject",{
"AdmatrixId" : ss['AdmatrixId'],
"token" : token,
"script_version" : script_version,
"addon_version" : ss['addon-version'],
"title_view_enable" : title_view_enable,
"exceptions_enable" : exceptions_enable,
"state" : ss['state' + worker.tab.id],
"update" : update
});
}
}