SlideShare une entreprise Scribd logo
1  sur  173
Télécharger pour lire hors ligne
Firefox Add-on SDK入門
Pasta-K
📧pastak@kmc.gr.jp
こんにちは😺
こんにちは😺
で、誰?
🍣 KMCでの活動
KMC 37代目 広報
OSC Kyoto 2014 出展 etc
JavaScriptで世界平和2014
🍣 インターネット活動
! twitter.com/pastak
id: Pasta-K
pastak.hatenablog.com
pastak-diary.hatenadiary.com
" github.com/pastak
はてなインターン 2013
アルバイトエンジニア ←イマココ
最近では、はてなのインターンを
経てウチへ来るというのが定番の
ようになっているんです
!
休学して社員となった杉本氏をは
じめ、現在5人いる学生アルバイ
トのメンバーも、そのほとんどが
「京大マイコンクラブ」の所属。
アニメファン
🐾目次
• Mozilla Firefox について
• ブラウザー拡張について
• Firefox Add-on SDKでアドオンを作ってみる
" サンプルコード
https://github.com/pastak/
firefox-addon-sdk-sample
Mozilla Firefox
について
About Mozilla Firefox
https://www.mozilla.org/firefox/
Mozillaによって提供されているブラウザ
Win / Mac / Linux / Android
現行最新バージョン 36.0.1 (2015/03/05)
次回37は2015/03/31にリリース予定
Netscapeがうにゃむにゃみたいな歴史に興味
がある人は自力でググッてください
ブラウザシェア (2015/02)
http://news.mynavi.jp/news/2015/03/04/084/
?
iceweasel
iceweasel
Iceweasel(アイスウィーズル)とは、ウェブブラウザ。または
その開発プロジェクト。Mozilla Firefox とほぼ同一のものであ
るが、商標の関係により同プロジェクトから独立したもの。
Debian によるものと、GNU によるもの(GNU IceWeasel)が
存在したが、現在では GNU IceWeasel は GNU IceCat に名称
変更されている。
http://ja.wikipedia.org/wiki/Iceweasel
ブラウザ拡張
現状確認
Google Chrome
Opera
(Based Chromium)
Google Chrome / Opera
JavaScript + HTML + CSS で書ける
Chromium由来の共通のAPIを利用する
→同じExtensionがそのまま動作する
Chrome ExtensionをOperaにインストールするための拡張
https://addons.opera.com/ja/extensions/details/download-
chrome-extension-9
ちなみにOpera側のドキュメントの方が少しだけ充実してる
!
ExtensionとAppの2種類がある
Safari
Safari
https://extensions.apple.com/
Chromiumと同じくHTML + JS + CSSで書ける
大体同じ世界観で書ける
ドキュメント
https://developer.apple.com/library/safari/
documentation/Tools/Conceptual/
SafariExtensionGuide/Introduction/Introduction.html
!
Internet Explorer
Internet Explorer
MSDNにチュートリアルなどのページがある。
(寧ろそれ以外どこにも載ってない)
Browser Extensions Overviews and Tutorials
https://msdn.microsoft.com/en-us/library/
aa753616%28v=vs.85%29.aspx
C#で書くっぽい。
あんまり書いてる人見たこと無い気がする
Mozilla Firefox
Mozilla Firefox
ダウンロード
https://addons.mozilla.org/
ドキュメントはとにかくMDN
https://developer.mozilla.org/ja/docs/Extensions
開発手法は2通り
XULベースのAdd-on
HTML + JS + CSSベースのAdd-on
Addon SDKで開発できるのは後者
このあと詳しく
Google Chrome
VS
Mozilla Firefox
操作可能範囲
表示領域
右上ポップアップ
オムニバー
表示領域
サイドバー
ツールバー
URLバー
ポップアップ
ステータスバー
表示領域
サイドバー
ツールバー
URLバー
ポップアップ
ステータスバー
Add-on SDKでステータスバーにボタンなど
を追加するためのwidgetモジュールがFirefox 29で削除
公開方法
https://chrome.google.com/webstore/
無審査
開発者登録に$5
https://addons.mozilla.org/
審査あり
開発者登録無料
審査あり
開発者登録無料
人が確認するのではない
セキュリティ上の問題などを自動確認
💖
Firefox
Add-on SDK
について
改めてFirefoxのアドオンについて
XULベースの開発とAdd-on SDKでの開発
配布形式は xpi ( その正体はzip )
!
開発に関しては困ったらとにかくMDNを見る
https://developer.mozilla.org/ja/docs/Extensions
http://www.slideshare.net/skeevs/mozilla-firefox-extension-development
XULベースの開発について
これ以降は特に触れない
Firefoxの初期からあった伝統的な方
FirefoxアプリケーションのXULを上書きして機能を提供し
たり、XPCOM コンポーネントを通じて操作をしたりす
る。
!
より詳細な比較は https://developer.mozilla.org/en-US/
Add-ons/SDK/Guides/SDK_vs_XUL を
2009年 Jetpackリリース
アドオンとして提供開始
当時JSだけでFirefoxのアドオンが作れると話題に
XULとか触らなくて良い
JSとHTMLとCSSでなんとかなる
jQueryも標準装備
Firebugでデバッグ出来る!
大喜び
– https://developer.mozilla.org/ja/docs/Jetpack
“Jetpack で、開発者は拡張機能を高速に
作り出すことができると同時に、強化さ
れた体験を与えられたユーザーは、Web
とのふれあいが変わるでしょう。”
それから
2010年 初頭
Jetpack(Prototype)から Jetpack Rebootへ
アドオンからSDKへ移行
開発環境もPythonで書かれたコマンドラインツールに
https://dev.mozilla.jp/2010/03/shifting-from-jetpack-
ptototype-to-jetpack-reboot/
!
2010年末名称を Add-on SDK へ名称変更 🎉
Add-on SDKについて
FirefoxだけではなくThumderbird向けのアド
オンも作れる
一部のAPIはAndroid向けのFirefoxにも対応
デバッグのためにUSBでインストール可能
Add-on SDKで扱えるAPI一覧
座学
ここまで
ここから
実践編
とにかく
数が多い
スライドに書いて
あること一覧
ボタンを出す
バッジを操作する
パネルを出す
サイドバーを出す
通信する
その他諸々
🚀
Add-on SDKのインストール
Mac OSXの人はhomebrewで一発
!
Git cloneでも入手可
!
!
https://developer.mozilla.org/en-US/Add-
ons/SDK/Tutorials/Installation
$ brew install mozilla-addon-sdk
$ git clone git@github.com:mozilla/addon-sdk.git
Add-on SDKのインストール
homebrew以外の方法で入れた場合は
./bin/ をPATHに追加する
と使えるようになる。
SDK仮想環境をシェル上で起動することが一
応推奨されているっぽい
$ bash ./bin/activate
$ cfx --version
Add-on SDK 1.17(12f7d53e8b5…)
cfxコマンドの基本的な使い方
Add-onのスケルトンを生成
!
Add-onをFirefox上で実行
!
Add-onをxpiとして生成
!
テストを実行
$ cfx init
$ cfx run
$ cfx xpi
$ cfx test
Add-on用のスケルトンを生成
!
!
!
!
!
この状態で      で実行可能
(ただし機能は何もない)
$ cfx init
.
├── data
├── lib
│   └── main.js
├── package.json
└── test
└── test-main.js
$ cfx run
$ cfx init
$ mkdir firefox-addon-sample
// cfx initは空のディレクトリでのみ実行可能
$ cd firefox-addon-sample
$ cfx init
* lib directory created
* data directory created
* test directory created
* generated jID automatically: *********
* package.json written
* test/test-main.js written
* lib/main.js written
!
Your sample add-on is now ready.
Do "cfx test" to test it and "cfx run" to try it. Have fun!
これを実行するとFirefoxが立ち上がる
実行する度にprofileを生成する
普段の環境を汚さない
profileを指定する場合は
$ cfx run
$ cfx run -p <ProfilePath>
<ProfilePath>
Mac OSXの場合は /Library/Application Support/Firefox/Profiles/以下にある
https://support.mozilla.org/ja/kb/profiles-where-firefox-stores-user-data
デバッグについて
JSで console.log すると標準出力に出る
デバッガーも利用可能
about:addons で [デバッグ]ボタンを押す
配布用のxpiファイルを生成
ドラッグアンドドロップでインストール
自前サーバで配布する場合でもアップデートを配信することも出来る
!
!
!
!
HTTPで配信する場合はこの方法では署名できない
詳細: Add-on SDKで作ったFirefox拡張をHTTPで配布、アップデートする方法 - Pastalablog in はてな
http://pastak.hatenablog.com/entry/2013/10/03/213040
!
※AMO以外からは配布出来なくなる予定があるので注意(おまけ参照)
$ cfx xpi
$ cfx xpi 
--update-url=https://hogehoge.com/hoge.update.rdf 
--update-link=https://hogehoge.com/hoge.xpi
ファイルの紹介
.
├── data <- 静的htmlや画像ファイルなど
│ アイコンなどもここに置く
├── lib
│   └── main.js <- エントリーポイント
│ になるJSファイル
├── package.json <- 設定などを書く
└── test <- ここに置いておけば
│ cfx test で実行してくれる
└── test-main.js
package.json
Add-onに関する情報を書いておくファイル。
$ cat package.json
{
"name": "sample",
"title": "sample",
"id": "jid1-ZaoDi2iCqJeZVg",
"description": "a basic add-on",
"author": "",
"license": "MPL 2.0",
"version": "0.1"
}
package.json
name:
アドオンの名前を入れる。
ドット(.)やスペースを含むことが出来ない。
title や fullName があればそちらが利用される
version:
アドオンのバージョンを書く。
記法は npm などと同じくsemver に準拠する
http://semver.org/
package.json
permissions:
クロスドメインXHRを許可するホスト名の指定
private browsing mode での許可
icon, icon64:
アイコンのパスを指定する
iconは48x48、icon64は64x64
無ければデフォルトアイコンを利用する
main.js を編集してみる
右上にボタンを追加してみる
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
モジュールを読み込む
モジュールの実体は
build時にxpiに含まれる
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
モジュールのパス
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
ボタンにidとか
アイコンとか
ハンドラを設定する。
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
相対パスで書くと
./data以下を参照する
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
ツールバー
18x18 (px)
メニューパネル
32x32 (px)
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
ツールバー
18x18 (px)
メニューパネル
32x32 (px)
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: "./icon-16.png",
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
省略可
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
}
クリック時に呼ばれる関数
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick
});
!
function handleClick(state) {
tabs.open("http://www.mozilla.org/");
} 新しいタブを開く
badgeを出す
New feature for Firefox 36
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
}
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
}
変更箇所
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
}
badgeの値と色を指定
値: Number or String
色: CSS color value
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: "KMC!!",
badgeColor: "#00AAAA"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
}
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: "KMC!!",
badgeColor: "blue"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
}
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
!
var button = buttons.ActionButton({
id: "mozilla-link",
label: "Visit Mozilla",
icon: {
"16": "./icon-16.png",
"32": "./icon-32.png",
"64": "./icon-64.png"
},
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
function handleClick(state) {
button.badge = state.badge + 1;
} badgeの値を変更
ボタンを押したらパネルが出る
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
panelモジュール
main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
パネルを記述
main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
width, height
大きさ
contextMenu
コンテキストメニューの表示の許可
onShow / onHide / onMessage
各々のイベントハンドラー main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
表示するHTMLファイルを指定
main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
パネルを表示
main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show({
position: button
});
}
button.state('window', {checked: !state.checked});
}
object
top / bottom / left / right
main.js
var buttons = require('sdk/ui/button/action');
var panels = require("sdk/panel");
!
var button = buttons.ActionButton({
id: "panel-sample",
label: "Panel Button",
icon: "./icon-16.png",
onClick: handleClick,
badge: 0,
badgeColor: "#00AAAA"
});
!
var panel = panels.Panel({
contentURL: "./panel.html",
position: button,
onHide: function(){
button.state('window', {checked: false});
}
});
!
function handleClick(state) {
if (!state.checked) {
panel.show();
}
button.state('window', {checked: !state.checked});
}
ここで設定しておいてもOK
main.js
パネル内のイベントを掴む
パネル内のHTML上のボタンが押されたら
badgeの内容をアップデートする
Messaging(1) - port
emit(name, message)
on(name, handler)
removeListener(name, handler)
once(name, handler)
一度だけmessageを受ける
事前にmessageに対してhandlerを設定しておく
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_port
Messaging(2) - postMessage
messageイベントを発火させる
context-menuモジュールなどはportがサポートさ
れていないのでこちらを使うしかない
panelなどには onMessage があるので、こっちを
使っておくと吉
https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts/using_postMessage
!
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
},
onMessage: function(){
button.badge = button.badge + 1;
}
});
!
main.js(抜粋)
!
!
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
},
onMessage: function(){
button.badge = button.badge + 1;
}
});
!
メッセージを受取ったら
badgeを++
main.js(抜粋)
<h1>Panel!!!!!!!</h1>
<button id='increament'>++</button>
<script>
!
!
!
</script>
var btn = document.getElementById('increament');
btn.addEventListener('click', function(){
addon.postMessage("increament");
})
./data/panel.html
この場合はpostMessageは
addonのメソッド
JSをcontentScriptとして
読み込ませた場合はselfのメソッドになる
var btn = document.getElementById('increament');
btn.addEventListener('click', function(){
addon.postMessage({
type: 'increament'
});
})
var color = document.getElementById('color');
color.addEventListener('change', function(){
addon.postMessage({
type: 'color',
value: color.value
});
})
<h1>Panel!!!!!!!</h1>
<button id='increament'>++</button>
<input type='color' id='color'/>
<script>
!
!
!
!
!
!
!
!
!
!
!
!
</script>
./data/panel.html
input[type=color]を使って
badgeの色を変える
var btn = document.getElementById('increament');
btn.addEventListener('click', function(){
addon.postMessage({
type: 'increament'
});
})
var color = document.getElementById('color');
color.addEventListener('change', function(){
addon.postMessage({
type: 'color',
value: color.value
});
})
<h1>Panel!!!!!!!</h1>
<button id='increament'>++</button>
<input type='color' id='color'/>
<script>
!
!
!
!
!
!
!
!
!
!
!
!
</script>
./data/panel.html
messageはJSONserializeされる
var panel = panels.Panel({
contentURL: "./panel.html",
onHide: function(){
button.state('window', {checked: false});
},
onMessage: function(message){
switch(message.type){
case 'increament':
button.badge = button.badge + 1;
break;
case 'color':
button.badgeColor = message.value;
break;
}
}
});
main.js(抜粋)
message.typeで挙動を切替
サイドバーを表示する
サイドバー
Chrome Extensionだとこれが出来ない
扱い方の要領はpanelと同じ
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html'
})
main.js
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html'
})
main.js
main.js
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
worker.port.emit('ping', '');
}
})
main.js
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
worker.port.emit('ping', '');
}
}) サイドバーが表示されたら
messageを送る
<script>
!
!
!
</script>
addon.port.on('ping', function(){
alert('pong');
})
./data/sidebar.html
<script>
!
!
!
</script>
addon.port.on('ping', function(){
alert('pong');
})
./data/sidebar.html
alertを出す
サイドバーでAjaxを実装する
サイドバーからはCross-Originの制約でXHRなどで
外部に通信できない
panelなどはJSをファイルに分けて、
contentScriptFileなどで読込めばXHR出来る
→main.jsで代わりに通信して結果を
 messageで送信する
package.jsonに追記
通信先のプロトコルとドメインを記述する
Wildcardは使えないので注意
"permissions": {
"cross-domain-content": [
"http://example.org/",
"https://example.com/"
]
}
Request = require("sdk/request").Request;
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
Request({
url: "http://api.openweathermap.org/data/2.5/weather?
q=Kyoto,jp",
// 天気情報を取得するAPIを利用してみる
onComplete: function(response){
worker.port.emit('ping', response.json)
}
}).get()
}
})
main.js
Request = require("sdk/request").Request;
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
Request({
url: "http://api.openweathermap.org/data/2.5/weather?
q=Kyoto,jp",
// 天気情報を取得するAPIを利用してみる
onComplete: function(response){
worker.port.emit('ping', response.json)
}
}).get()
}
})
main.js
外部と通信するために
Requestモジュールを利用
Request
XMLHttpRequestをベースにしたオブジェクト
!
option:
url: 通信先のURL
onComplete: 通信成功後に呼ぶ関数
content: GETやHEADのquery、POSTやPUTのbody
contentType: request headerのContent-Typeの内容
header: request header
require("sdk/request").Request(option)
Request methods
例: GET
!
get()
head()
post()
put()
delete()
require("sdk/request").Request(option).get()
Request = require("sdk/request").Request;
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
Request({
url: "http://api.openweathermap.org/data/2.5/weather?
q=Kyoto,jp",
// 天気情報を取得するAPIを利用してみる
onComplete: function(response){
worker.port.emit('ping', response.json)
}
}).get()
}
})
main.js
成功したらportで
サイドバー内のJSに送る
Request = require("sdk/request").Request;
sideBar = require("sdk/ui/sidebar").Sidebar({
id: 'sample-sidebar',
title: 'MiracleSidebar',
url: './sidebar.html',
onAttach: function(worker){
Request({
url: "http://api.openweathermap.org/data/2.5/weather?
q=Kyoto,jp",
// 天気情報を取得するAPIを利用してみる
onComplete: function(response){
worker.port.emit('ping', response.json)
}
}).get()
}
})
main.js
通信結果を保持しているオブジェクト
text: plain text
json: JSON.parse()の結果
status: status code ( ex: 200 )
statusText:
headers: HTTP response headerのK/Vオブジェクト
<script>
!
!
!
!
!
!
!
!
!
!
!
</script>
addon.port.on('ping', function(data){
var weather = data.weather[0];
document.body.innerHTML = `
<ul>
<li>Weather: ${weather.main}</li>
<li>Description: ${weather.description}</li>
<li>Sunrise: ${Date(data.sys.sunrise)} </li>
<li>Sunset: ${Date(data.sys.sunset)}</li>
</ul>
`;
})
./data/sidebar.html
<script>
!
!
!
!
!
!
!
!
!
!
!
</script>
addon.port.on('ping', function(data){
var weather = data.weather[0];
document.body.innerHTML = `
<ul>
<li>Weather: ${weather.main}</li>
<li>Description: ${weather.description}</li>
<li>Sunrise: ${Date(data.sys.sunrise)} </li>
<li>Sunset: ${Date(data.sys.sunset)}</li>
</ul>
`;
})
./data/sidebar.html
メッセージを受け取って
HTMLとして出力
諸々モジュール紹介
clipboard, context-menu, hotkeys,
notification, page-mod, simple-prefs …
clipboard
var clipboard = require("sdk/clipboard");
clipboard.set("KMC is great!");
var contents = clipboard.get();
//set some HTML
clipboard.set("<marquee>KMC KMC KMC</marquee>", "html");
context-menu
コンテキストメニューを表示する
Chromeと違って細やかに設定できる
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
メニューアイテム
を生成
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
表示するテキスト
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
アイコンを出すことも
出来る
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
表示するコンテキストを
指定する
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
こういう指定も出来る
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
PreficateContextは
引数の関数がtrueを返す場合に
メニューアイテムを表示する
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
PreficateContextは
引数の関数がtrueを返す場合に
メニューアイテムを表示する
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
documentType
text/html, image/jpeg
documentURL
ページのURL
targetName
タグ名
targetID
id属性
isEditable
contenteditable?
selectionText
選択中の文字列
srcURL
<img>のsrc属性
linkURL
<a>のhref属性
value
<input><textarea>のvalue属性
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
MenuはItemを
子に持つことが出来る
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
ページ上のあらゆる箇所
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
child, sp, child2を
子メニューとして持つ
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
セパレータを生成
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
親メニューの無いアイテム
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
メニューを表示している状態で
このキーを押すと選択したことになる。
キーの組み合わせ不可
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
}); URLでマッチさせる
var cm = require("sdk/context-menu");
var sp = cm.Separator();
var child = cm.Item({
label: "Child Item 2 show only on <img>",
image: require("sdk/self").data.url("icon-16.png")
});
child.context.add(cm.SelectorContext('img'));
var child2 = cm.Item({
label: "This item show only on some context",
context: cm.PredicateContext(function(context){
return (
(context.documentURL.match(/kmc/)) ||
(context.selectionText && context.selectionText.length > 3) ||
(context.srcURL && context.srcURL.match(/png$/))
)
})
});
var menu = cm.Menu({
label: "sample menu",
context: cm.PageContext(),
items: [child, sp, child2]
})
var menu2 = cm.Item({
label: "Welcome KMC",
accesskey: '1',
context: cm.URLContext("*.kmc.gr.jp"),
contentScriptFile: './welcome-kmc.js'
});
選択された際に実行されるJS
var { Hotkey } = require("sdk/hotkeys");
!
var myPanel = require("sdk/panel").Panel({
...
});
!
var showHotKey = Hotkey({
combo: "accel-shift-o",
onPress: function() {
myPanel.show();
}
});
hotkeys
hotkeys
alt: [alt]、Macでは [option] キー
meta: [Meta] ([#] キー)、Macでは [⌘]
accel: [ctrl]、Macでは[⌘]
shift, control, pageup, pagedown
notification
var notifications = require("sdk/notifications");
!
notifications.notify({
title: "Mailer",
text: "Got new 2 mails!",
onClick: function(){
console.log('clicked')
},
iconURL: "./myIcon.png"
});
page-mod
user.jsなどのように閲覧しているページ内に
JSを埋め込む。
content script と呼ばれることが多い
page-mod
var pageMod = require("sdk/page-mod");
!
pageMod.PageMod({
include: "*",
exclude: "*.kmc.gr.jp",
contentScriptFile: [
"./jquery-1.7.min.js",
"./my-script.js"
]
});
page-mod
content script 内で port や postMessage を使
う場合は window.self のメソッドとして利用
できる
simple-prefs
設定画面にボタンなどを配置する
package.json に記述しておく
"preferences": [{
"name": "somePreference",
"title": "Some preference title",
"description": "description text",
"type": "string",
"value": "this is the default string value"
}]
simple-prefs
type: 種別を指定(後述)
name: JS内で利用するための名前
   (propaty name として valid な必要がある)
title: 設定画面でラベルに使われる
description: 設定に関する説明
hidden: 非表示にする ( boolean )
value: デフォルト値
simple-prefs
値を取り出す時は name を使う
"preferences": [{
"name": "somePreference",
"type": "string",
"value": "this is the default string value"
}]
var preferences = require("sdk/simple-prefs").prefs;
// Get
console.log(preferences.somePreference);
// Update
preferences.somePreference = "this is a new value";
package.json
main.js
type (1)
bool: <input type='checkbox'>
boolint: checkbox が表示されるが値はT/Fで
はなくて on off の値が利用される
"preferences": [{
"type": "boolint",
"on": "1", // value for true must be string
"off": "2", // value for false must be string
"value": 1
}]
type (2)
integer: <input type='number'>
string: <input type='text'>
color: <input type='color'>
file: <input type='file'> フルパスを得る
directory: ディレクトリのパスを得る
type (3)
menulist: ドロップダウンリストが表示される
radio: <input type="radio">
{
"type": "menulist", // or "radio"
"options": [
{
"value": "0", //must be string
"label": "nona"
},
{
"value": "1",
"label": "prime"
}
]
}
type (4)
control: <button> {
"type": "control",
"label": "Click me!",
"name": "sayHello",
"title": "Say Hello"
}
var sp = require("sdk/simple-prefs");
sp.on("sayHello", function() {
console.log("hello");
});
package.json
main.js
type (4)
control: <button> {
"type": "control",
"label": "Click me!",
"name": "sayHello",
"title": "Say Hello"
}
var sp = require("sdk/simple-prefs");
sp.on("sayHello", function() {
console.log("hello");
});
package.json
main.js
type (4)
control: <button> {
"type": "control",
"label": "Click me!",
"name": "sayHello",
"title": "Say Hello"
}
var sp = require("sdk/simple-prefs");
sp.on("", function() {
console.log("hello");
});
package.json
main.js
空にすると全てのcontrollの
clickを受け取る
まとめ
ズサッと一気に Add-on SDK の世界観を紹介しました。
サンプルコードも用意したので、MDNなどと合わせて
上手く活用して良いAdd-onを作って下さい。
Firefoxをより便利にして
最高のインターネットライフを
手に入れよう😍😍😍😍
Do you have any questions?🙉
おまけ
※時間が余った時など用
野良アドオン
について
公式配布サイト以外に関する対応
Chrome
2014/05 から Windows 向けの Stable と
Beta チャンネルのみ外部サイトからインス
トールした Extension は無効化
Firefox
AMO以外で配布する場合は要署名
Nightly と Developer Edition は除外
http://googledevjp.blogspot.jp/2014/03/chrome-chrome.html
https://dev.mozilla.jp/2015/02/extension-signing-safer-experience/
Firefoxにおける影響
アドオンのテストを行う場合はDeveloper Edition、
Nightly あるいはいずれかのノーブランド版で行う必
要がある。
AMOで配布しない場合も一度署名のために AMO へ
拡張機能ファイルをアップロードするという必須手
順が導入される。

Contenu connexe

Tendances

はじめてのCouch db
はじめてのCouch dbはじめてのCouch db
はじめてのCouch dbEiji Kuroda
 
3分でサーバオペレーションコマンドを作る技術
3分でサーバオペレーションコマンドを作る技術3分でサーバオペレーションコマンドを作る技術
3分でサーバオペレーションコマンドを作る技術Kei IWASAKI
 
20120516 第7回ウフィカ社内ハンズオン Git基礎
20120516 第7回ウフィカ社内ハンズオン Git基礎20120516 第7回ウフィカ社内ハンズオン Git基礎
20120516 第7回ウフィカ社内ハンズオン Git基礎Makoto Yamazaki
 
Ctb57 with god7
Ctb57 with god7Ctb57 with god7
Ctb57 with god7kingtomo
 
BPStudy32 CouchDB 再入門
BPStudy32 CouchDB 再入門BPStudy32 CouchDB 再入門
BPStudy32 CouchDB 再入門Yohei Sasaki
 
長いの
長いの長いの
長いのemasaka
 
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12Takanori Suzuki
 
Git pyfes201207-presen
Git pyfes201207-presenGit pyfes201207-presen
Git pyfes201207-presenKouhei Maeda
 
JSのお勉強の話。
JSのお勉強の話。JSのお勉強の話。
JSのお勉強の話。Hiroshi Urabe
 
ocamloptの全体像
ocamloptの全体像ocamloptの全体像
ocamloptの全体像Kiwamu Okabe
 
菩薩でもわかる!Rで動かすExcelアドインの作り方
 菩薩でもわかる!Rで動かすExcelアドインの作り方  菩薩でもわかる!Rで動かすExcelアドインの作り方
菩薩でもわかる!Rで動かすExcelアドインの作り方 Nagi Teramo
 
明日から使える気になるGo言語による並行処理
明日から使える気になるGo言語による並行処理明日から使える気になるGo言語による並行処理
明日から使える気になるGo言語による並行処理Yuto Matsukubo
 
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hackyut148atgmaildotcom
 
Pelican の紹介 / World Plone Day 2017 Tokyo
Pelican の紹介 / World Plone Day 2017 TokyoPelican の紹介 / World Plone Day 2017 Tokyo
Pelican の紹介 / World Plone Day 2017 TokyoKei IWASAKI
 
Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Jun Ohtani
 
Djangoによるスマホアプリバックエンドの実装
Djangoによるスマホアプリバックエンドの実装Djangoによるスマホアプリバックエンドの実装
Djangoによるスマホアプリバックエンドの実装Nakazawa Yuichi
 
How Smalltalker Works
How Smalltalker WorksHow Smalltalker Works
How Smalltalker WorksSho Yoshida
 
Mojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてるMojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてるjamadam
 
Redis Intro Osc2010 Tokyo Spring
Redis Intro Osc2010 Tokyo SpringRedis Intro Osc2010 Tokyo Spring
Redis Intro Osc2010 Tokyo SpringMakoto Ohnami
 

Tendances (20)

はじめてのCouch db
はじめてのCouch dbはじめてのCouch db
はじめてのCouch db
 
3分でサーバオペレーションコマンドを作る技術
3分でサーバオペレーションコマンドを作る技術3分でサーバオペレーションコマンドを作る技術
3分でサーバオペレーションコマンドを作る技術
 
20120516 第7回ウフィカ社内ハンズオン Git基礎
20120516 第7回ウフィカ社内ハンズオン Git基礎20120516 第7回ウフィカ社内ハンズオン Git基礎
20120516 第7回ウフィカ社内ハンズオン Git基礎
 
Ctb57 with god7
Ctb57 with god7Ctb57 with god7
Ctb57 with god7
 
BPStudy32 CouchDB 再入門
BPStudy32 CouchDB 再入門BPStudy32 CouchDB 再入門
BPStudy32 CouchDB 再入門
 
長いの
長いの長いの
長いの
 
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
「Python言語」はじめの一歩 / First step of Python / 2016 Jan 12
 
Git pyfes201207-presen
Git pyfes201207-presenGit pyfes201207-presen
Git pyfes201207-presen
 
JSのお勉強の話。
JSのお勉強の話。JSのお勉強の話。
JSのお勉強の話。
 
ocamloptの全体像
ocamloptの全体像ocamloptの全体像
ocamloptの全体像
 
菩薩でもわかる!Rで動かすExcelアドインの作り方
 菩薩でもわかる!Rで動かすExcelアドインの作り方  菩薩でもわかる!Rで動かすExcelアドインの作り方
菩薩でもわかる!Rで動かすExcelアドインの作り方
 
明日から使える気になるGo言語による並行処理
明日から使える気になるGo言語による並行処理明日から使える気になるGo言語による並行処理
明日から使える気になるGo言語による並行処理
 
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack
配布用Dotcloudによるすぐ始めるtwitterwebアプリ開発#twtr hack
 
Pelican の紹介 / World Plone Day 2017 Tokyo
Pelican の紹介 / World Plone Day 2017 TokyoPelican の紹介 / World Plone Day 2017 Tokyo
Pelican の紹介 / World Plone Day 2017 Tokyo
 
Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207Elasticsearch入門 pyfes 201207
Elasticsearch入門 pyfes 201207
 
後期02
後期02後期02
後期02
 
Djangoによるスマホアプリバックエンドの実装
Djangoによるスマホアプリバックエンドの実装Djangoによるスマホアプリバックエンドの実装
Djangoによるスマホアプリバックエンドの実装
 
How Smalltalker Works
How Smalltalker WorksHow Smalltalker Works
How Smalltalker Works
 
Mojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてるMojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてる
 
Redis Intro Osc2010 Tokyo Spring
Redis Intro Osc2010 Tokyo SpringRedis Intro Osc2010 Tokyo Spring
Redis Intro Osc2010 Tokyo Spring
 

En vedette

Gaucheでマクロを書こう
Gaucheでマクロを書こうGaucheでマクロを書こう
Gaucheでマクロを書こうHideaki Nagamine
 
暗号技術入門 秘密の国のアリス 総集編
暗号技術入門 秘密の国のアリス 総集編暗号技術入門 秘密の国のアリス 総集編
暗号技術入門 秘密の国のアリス 総集編京大 マイコンクラブ
 
FM音源をいじれるWebサービスを作った
FM音源をいじれるWebサービスを作ったFM音源をいじれるWebサービスを作った
FM音源をいじれるWebサービスを作ったCHY72
 
数値解析と物理学
数値解析と物理学数値解析と物理学
数値解析と物理学すずしめ
 
タイ文字と若干情報科学[修正版]
タイ文字と若干情報科学[修正版]タイ文字と若干情報科学[修正版]
タイ文字と若干情報科学[修正版]. きぷ
 
フォントの選び方・使い方
フォントの選び方・使い方フォントの選び方・使い方
フォントの選び方・使い方k maztani
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.kiki utagawa
 
AWSに移行して本当によかったです(小並感)
AWSに移行して本当によかったです(小並感)AWSに移行して本当によかったです(小並感)
AWSに移行して本当によかったです(小並感)Akinori Tsuboi
 
初心者のためのマクロZerg講座
初心者のためのマクロZerg講座初心者のためのマクロZerg講座
初心者のためのマクロZerg講座a8085
 
2016年はFitbitで生体データを監視元年!
2016年はFitbitで生体データを監視元年!2016年はFitbitで生体データを監視元年!
2016年はFitbitで生体データを監視元年!Shoot Morii
 
部の紹介_テンプレート
部の紹介_テンプレート部の紹介_テンプレート
部の紹介_テンプレート_ uruchan1997
 
AWSをコードで定義する
AWSをコードで定義するAWSをコードで定義する
AWSをコードで定義するSugawara Genki
 

En vedette (20)

ECMAScript没proposal追悼式
ECMAScript没proposal追悼式ECMAScript没proposal追悼式
ECMAScript没proposal追悼式
 
Pietのエディタを作った話
Pietのエディタを作った話Pietのエディタを作った話
Pietのエディタを作った話
 
Topological data analysis
Topological data analysisTopological data analysis
Topological data analysis
 
SSL で守られる生活
SSL で守られる生活SSL で守られる生活
SSL で守られる生活
 
Gaucheでマクロを書こう
Gaucheでマクロを書こうGaucheでマクロを書こう
Gaucheでマクロを書こう
 
GoでMinecraftっぽいの作る
GoでMinecraftっぽいの作るGoでMinecraftっぽいの作る
GoでMinecraftっぽいの作る
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 
暗号技術入門 秘密の国のアリス 総集編
暗号技術入門 秘密の国のアリス 総集編暗号技術入門 秘密の国のアリス 総集編
暗号技術入門 秘密の国のアリス 総集編
 
Altseed
AltseedAltseed
Altseed
 
FM音源をいじれるWebサービスを作った
FM音源をいじれるWebサービスを作ったFM音源をいじれるWebサービスを作った
FM音源をいじれるWebサービスを作った
 
数値解析と物理学
数値解析と物理学数値解析と物理学
数値解析と物理学
 
タイ文字と若干情報科学[修正版]
タイ文字と若干情報科学[修正版]タイ文字と若干情報科学[修正版]
タイ文字と若干情報科学[修正版]
 
フォントの選び方・使い方
フォントの選び方・使い方フォントの選び方・使い方
フォントの選び方・使い方
 
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
Pythonの処理系はどのように実装され,どのように動いているのか? 我々はその実態を調査すべくアマゾンへと飛んだ.
 
AWSに移行して本当によかったです(小並感)
AWSに移行して本当によかったです(小並感)AWSに移行して本当によかったです(小並感)
AWSに移行して本当によかったです(小並感)
 
Webページで学ぶJavaScript2013 第1回
Webページで学ぶJavaScript2013 第1回Webページで学ぶJavaScript2013 第1回
Webページで学ぶJavaScript2013 第1回
 
初心者のためのマクロZerg講座
初心者のためのマクロZerg講座初心者のためのマクロZerg講座
初心者のためのマクロZerg講座
 
2016年はFitbitで生体データを監視元年!
2016年はFitbitで生体データを監視元年!2016年はFitbitで生体データを監視元年!
2016年はFitbitで生体データを監視元年!
 
部の紹介_テンプレート
部の紹介_テンプレート部の紹介_テンプレート
部の紹介_テンプレート
 
AWSをコードで定義する
AWSをコードで定義するAWSをコードで定義する
AWSをコードで定義する
 

Similaire à Firefox Add-on SDK 入門

Real world android akka
Real world android akkaReal world android akka
Real world android akkaTaisuke Oe
 
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会Mori Tetsuya
 
2008 08 09_osc2008nagoya_fight_cms_x_cube
2008 08 09_osc2008nagoya_fight_cms_x_cube2008 08 09_osc2008nagoya_fight_cms_x_cube
2008 08 09_osc2008nagoya_fight_cms_x_cubeTom Hayakawa
 
WordBench Saitama vol.6
WordBench Saitama vol.6WordBench Saitama vol.6
WordBench Saitama vol.6masaaki komori
 
Sphinx拡張 探訪 2014 #sphinxjp
Sphinx拡張 探訪 2014 #sphinxjpSphinx拡張 探訪 2014 #sphinxjp
Sphinx拡張 探訪 2014 #sphinxjpTakeshi Komiya
 
Secret of Firefox
Secret of FirefoxSecret of Firefox
Secret of Firefoxdynamis
 
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011Hiroh Satoh
 
Couchbase MeetUP Tokyo - #11 Omoidenote
Couchbase MeetUP Tokyo - #11 OmoidenoteCouchbase MeetUP Tokyo - #11 Omoidenote
Couchbase MeetUP Tokyo - #11 Omoidenotekitsugi
 
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会Takayuki Shimizukawa
 
Dive into the Cloud with our buddy, lovely PHP!
Dive into the Cloud with our buddy, lovely PHP!Dive into the Cloud with our buddy, lovely PHP!
Dive into the Cloud with our buddy, lovely PHP!Sotaro Omura
 
勉強会 Cvml python基礎
勉強会 Cvml python基礎勉強会 Cvml python基礎
勉強会 Cvml python基礎真哉 杉野
 
debexpo(mentors.d.n)をハックするには
debexpo(mentors.d.n)をハックするにはdebexpo(mentors.d.n)をハックするには
debexpo(mentors.d.n)をハックするにはkenhys
 
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築するピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築するTakahito Tejima
 
2012 0623-x-road-tokyo-xoops-x(ten)
2012 0623-x-road-tokyo-xoops-x(ten)2012 0623-x-road-tokyo-xoops-x(ten)
2012 0623-x-road-tokyo-xoops-x(ten)Naoki Okino
 
Metahub for github
Metahub for githubMetahub for github
Metahub for githubSuguru Oho
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Shogo Kawahara
 

Similaire à Firefox Add-on SDK 入門 (20)

Real world android akka
Real world android akkaReal world android akka
Real world android akka
 
Firefox OS アプリ開発
Firefox OS アプリ開発Firefox OS アプリ開発
Firefox OS アプリ開発
 
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会
Unity に於ける iOS ビルド自動化のおはなし - 20141030 第26回 Unity 勉強会
 
Firefoxosハンズオン
FirefoxosハンズオンFirefoxosハンズオン
Firefoxosハンズオン
 
2008 08 09_osc2008nagoya_fight_cms_x_cube
2008 08 09_osc2008nagoya_fight_cms_x_cube2008 08 09_osc2008nagoya_fight_cms_x_cube
2008 08 09_osc2008nagoya_fight_cms_x_cube
 
WordBench Saitama vol.6
WordBench Saitama vol.6WordBench Saitama vol.6
WordBench Saitama vol.6
 
Sphinx拡張 探訪 2014 #sphinxjp
Sphinx拡張 探訪 2014 #sphinxjpSphinx拡張 探訪 2014 #sphinxjp
Sphinx拡張 探訪 2014 #sphinxjp
 
Secret of Firefox
Secret of FirefoxSecret of Firefox
Secret of Firefox
 
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
ぼくのかんがえたさいきょうのうぇぶあぷりけーしょんふれーむわーく - YAPC Asia 2011
 
Haikara
HaikaraHaikara
Haikara
 
Couchbase MeetUP Tokyo - #11 Omoidenote
Couchbase MeetUP Tokyo - #11 OmoidenoteCouchbase MeetUP Tokyo - #11 Omoidenote
Couchbase MeetUP Tokyo - #11 Omoidenote
 
SocketStream入門
SocketStream入門SocketStream入門
SocketStream入門
 
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
ドキュメンテーションを加速するストレスフリーの作図ツール『blockdiag』 jus2011年6月勉強会
 
Dive into the Cloud with our buddy, lovely PHP!
Dive into the Cloud with our buddy, lovely PHP!Dive into the Cloud with our buddy, lovely PHP!
Dive into the Cloud with our buddy, lovely PHP!
 
勉強会 Cvml python基礎
勉強会 Cvml python基礎勉強会 Cvml python基礎
勉強会 Cvml python基礎
 
debexpo(mentors.d.n)をハックするには
debexpo(mentors.d.n)をハックするにはdebexpo(mentors.d.n)をハックするには
debexpo(mentors.d.n)をハックするには
 
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築するピクサー USD 入門 新たなコンテンツパイプラインを構築する
ピクサー USD 入門 新たなコンテンツパイプラインを構築する
 
2012 0623-x-road-tokyo-xoops-x(ten)
2012 0623-x-road-tokyo-xoops-x(ten)2012 0623-x-road-tokyo-xoops-x(ten)
2012 0623-x-road-tokyo-xoops-x(ten)
 
Metahub for github
Metahub for githubMetahub for github
Metahub for github
 
Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開Composer による依存管理 と Packagist によるライブラリの公開
Composer による依存管理 と Packagist によるライブラリの公開
 

Plus de Shoot Morii

Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都
Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都
Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都Shoot Morii
 
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門Shoot Morii
 
とにかくシュッと検索っぽいことをしたい!
とにかくシュッと検索っぽいことをしたい!とにかくシュッと検索っぽいことをしたい!
とにかくシュッと検索っぽいことをしたい!Shoot Morii
 
Don't you want to use Gyazo on Nintendo 3DS etc ?
Don't you want to use Gyazo on Nintendo 3DS etc ?Don't you want to use Gyazo on Nintendo 3DS etc ?
Don't you want to use Gyazo on Nintendo 3DS etc ?Shoot Morii
 
はてなインターン自己紹介
はてなインターン自己紹介はてなインターン自己紹介
はてなインターン自己紹介Shoot Morii
 
Matcha139 26th-lt
Matcha139 26th-ltMatcha139 26th-lt
Matcha139 26th-ltShoot Morii
 
情報と社会レポート1
情報と社会レポート1 情報と社会レポート1
情報と社会レポート1 Shoot Morii
 
Pasta kの取扱説明書
Pasta kの取扱説明書Pasta kの取扱説明書
Pasta kの取扱説明書Shoot Morii
 
Ict challenge+r2011 final
Ict challenge+r2011 finalIct challenge+r2011 final
Ict challenge+r2011 finalShoot Morii
 
Osc神戸学生lt
Osc神戸学生ltOsc神戸学生lt
Osc神戸学生ltShoot Morii
 
セキュリティ&プログラミングキャンプに参加してから
セキュリティ&プログラミングキャンプに参加してからセキュリティ&プログラミングキャンプに参加してから
セキュリティ&プログラミングキャンプに参加してからShoot Morii
 
高校生がイベントを作るということ
高校生がイベントを作るということ高校生がイベントを作るということ
高校生がイベントを作るということShoot Morii
 
高校生がイベントを作るということ
高校生がイベントを作るということ高校生がイベントを作るということ
高校生がイベントを作るということShoot Morii
 
XML Http Request Level2 の噂を聞いたので調べてみた
XML Http Request Level2 の噂を聞いたので調べてみたXML Http Request Level2 の噂を聞いたので調べてみた
XML Http Request Level2 の噂を聞いたので調べてみたShoot Morii
 
学生とItコミュニティの現在(いま)
学生とItコミュニティの現在(いま)学生とItコミュニティの現在(いま)
学生とItコミュニティの現在(いま)Shoot Morii
 
まっちゃ139ー学生とITコミュ二ティの現在
まっちゃ139ー学生とITコミュ二ティの現在まっちゃ139ー学生とITコミュ二ティの現在
まっちゃ139ー学生とITコミュ二ティの現在Shoot Morii
 
G91関西忘年会Lt資料
G91関西忘年会Lt資料G91関西忘年会Lt資料
G91関西忘年会Lt資料Shoot Morii
 

Plus de Shoot Morii (20)

Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都
Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都
Gyazo for Linux 最新ビルド事情紹介 @ KMC学習発表会 in OSC京都
 
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門
JavaScriptでわくわくシンセサイザーっぽい音が鳴るやつを作る入門
 
とにかくシュッと検索っぽいことをしたい!
とにかくシュッと検索っぽいことをしたい!とにかくシュッと検索っぽいことをしたい!
とにかくシュッと検索っぽいことをしたい!
 
Don't you want to use Gyazo on Nintendo 3DS etc ?
Don't you want to use Gyazo on Nintendo 3DS etc ?Don't you want to use Gyazo on Nintendo 3DS etc ?
Don't you want to use Gyazo on Nintendo 3DS etc ?
 
Pastapy
PastapyPastapy
Pastapy
 
はてなインターン自己紹介
はてなインターン自己紹介はてなインターン自己紹介
はてなインターン自己紹介
 
Matcha139 26th-lt
Matcha139 26th-ltMatcha139 26th-lt
Matcha139 26th-lt
 
Shibuya xss lt
Shibuya xss ltShibuya xss lt
Shibuya xss lt
 
情報と社会レポート1
情報と社会レポート1 情報と社会レポート1
情報と社会レポート1
 
Pasta kの取扱説明書
Pasta kの取扱説明書Pasta kの取扱説明書
Pasta kの取扱説明書
 
Ict challenge+r2011 final
Ict challenge+r2011 finalIct challenge+r2011 final
Ict challenge+r2011 final
 
Gitの紹介
Gitの紹介Gitの紹介
Gitの紹介
 
Osc神戸学生lt
Osc神戸学生ltOsc神戸学生lt
Osc神戸学生lt
 
セキュリティ&プログラミングキャンプに参加してから
セキュリティ&プログラミングキャンプに参加してからセキュリティ&プログラミングキャンプに参加してから
セキュリティ&プログラミングキャンプに参加してから
 
高校生がイベントを作るということ
高校生がイベントを作るということ高校生がイベントを作るということ
高校生がイベントを作るということ
 
高校生がイベントを作るということ
高校生がイベントを作るということ高校生がイベントを作るということ
高校生がイベントを作るということ
 
XML Http Request Level2 の噂を聞いたので調べてみた
XML Http Request Level2 の噂を聞いたので調べてみたXML Http Request Level2 の噂を聞いたので調べてみた
XML Http Request Level2 の噂を聞いたので調べてみた
 
学生とItコミュニティの現在(いま)
学生とItコミュニティの現在(いま)学生とItコミュニティの現在(いま)
学生とItコミュニティの現在(いま)
 
まっちゃ139ー学生とITコミュ二ティの現在
まっちゃ139ー学生とITコミュ二ティの現在まっちゃ139ー学生とITコミュ二ティの現在
まっちゃ139ー学生とITコミュ二ティの現在
 
G91関西忘年会Lt資料
G91関西忘年会Lt資料G91関西忘年会Lt資料
G91関西忘年会Lt資料
 

Dernier

UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdffurutsuka
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxAtomu Hidaka
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000Shota Ito
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。iPride Co., Ltd.
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directoryosamut
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 

Dernier (9)

UPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdfUPWARD_share_company_information_20240415.pdf
UPWARD_share_company_information_20240415.pdf
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptxIoT in the era of generative AI, Thanks IoT ALGYAN.pptx
IoT in the era of generative AI, Thanks IoT ALGYAN.pptx
 
PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000PHP-Conference-Odawara-2024-04-000000000
PHP-Conference-Odawara-2024-04-000000000
 
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
Amazon SES を勉強してみる その12024/04/12の勉強会で発表されたものです。
 
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
新人研修のまとめ       2024/04/12の勉強会で発表されたものです。新人研修のまとめ       2024/04/12の勉強会で発表されたものです。
新人研修のまとめ 2024/04/12の勉強会で発表されたものです。
 
20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory20240412_HCCJP での Windows Server 2025 Active Directory
20240412_HCCJP での Windows Server 2025 Active Directory
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 

Firefox Add-on SDK 入門