#はじめに
「オープンソースのソフトウェアをいじってみる」という課題が大学で出され、無謀にもMozilla Firefoxに挑戦してみました。この投稿もその一環です。
この課題は2人1組で取り組んだためこの投稿も2人で書いています。そのため以下の記述は2人の環境差による一貫していない部分があると思いますがご容赦ください。
##環境・バージョン
- Ubuntu 14.04 LTS 64bit
- mozilla-release ver.???
-
リンク<- 切れてます - 2015/10/26時点での最新版のはずですが、内部にversionを示す部分が見当たらず、リンクも切れており不明です(圧縮ファイルは消してしまいました )
-
##既存機能と目標
タイトルにもあるようにタブの切り替えについていじりたいと思います。_Firefox_は元々強力なタブ機能を備えており、タブ間の移動も右にCtrl + Tab、左にCtrl + Shift + Tabで切り替えできます。またタブグループというタブをまとめる機能も存在し、Ctrl + Shift + Eでグループのビューと編集が行える画面に移行します。**Ctrl + Shift + @やCtrl + Shit + ^**でグループ移動もできます。
もはや機能として十分な気もしますが、これを改造して
- Ctrl + カーソルキーで
- グループ内移動もグループ移動も
- プレビューを見ながら
タブ切り替えできるようにしてみたいと思います。
#実装までの道のり
##ビルド
まず、「Firefox の簡単なビルド方法」を参考にmozilla-release/.mozconfigを
. $topsrcdir/browser/config/mozconfig
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../../firefox_build
ac_add_options --enable-debug
このように作成し、mozilla-release内で
make -f client.mk
でビルドしようとしましたが、ここでいきなり問題が発生しました。
sudo apt-get build-dep firefox
で依存パッケージをインストールできず、
sudo apt-get upadte
も途中でエラーを起こして止まってしまいます。
→これはjp.ubuntuのサーバーがおかしいようで、他のサーバーに変えると無事依存パッケージのインストールが行えました。
上記の参考URLにしたがって*.mozconfig*を作成しビルドすると、gstreamer周りのパッケージが無く依存関係が満たせていないと言われました。
あれこれ試して、
apt-get install phonon-backend-gstreamer1.0
apt-get install gstremer1.0*
apt-get install gstreamer0.10
などでパッケージをインストールしてみましたが、相変わらず依存関係がgstreamer周りのパッケージがないと言われました。
しかし、.mozconfigにac_add_options --disable-gstreamer
を追加するとmake
が通るため、問題はやはりgstreamerパッケージがないことによる模様...
最終的に、以下のようにしてgstreamerパッケージをインストールすることに成功しました。
apt-get install libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev
このmake
が通った後、firefox_build/dist/firefoxから起動を確認できました。
##ソースを眺める
非常に大きなプロジェクトの中でいじるべき場所を見つけるべく、それらしい文字列で
grep -rn 'hogehoge'
を繰り返し、文字列の存在場所を見ていきます。
本筋と関係ありませんが
grep -rch ''
で各ファイルの行数が表示されます。この出力された数字を全部足してみると2000万行を超えました。このプロジェクトがいかに大きいか分かります。
事前情報によりbrowser.jsファイルをいじるとよいのではないかという希望があったため、探してみると同名のファイルがたくさんヒットしました。しかもみんな中身が違います。
色々といじってみた結果、mozila_release/browser/base/content/browser.jsに新しくタブを開く関数らしきものを見つけたため、中身をコメントアウトしてビルドして実行してみたところ、新しくタブを開けなくなりました。しばらくこのファイルをいじってみます。
grep
をしていくうちにmozilla-release/browser/base/content/browser-sets.incでショートカットキーと呼び出す関数を対応づけていることを発見しました。そして呼び出される関数の多くはbrowser.js内に定義されています。この二つをいじることで実際に挙動も変化し、逆にショートカットでよばれる関数を追うことで、既存の機能を担当している関数を調べることができます。
##タブに関するクラス・関数
ようやくオリジナル関数の作成に取り掛かります。
タブに関するjsファイルがたくさんあるのでそれぞれがどういう依存関係なのか、何が記述されているのか理解するのに時間がかかりました。
groupitem
やgroupitems
、panorama
(グループアイテムビューの旧名称だそうです)といったワードをgrep
し、検索結果からそれらしいファイルをピックアップしていきます。
その結果、
browser/components/tabview/groupitems.js
にGroupItem
クラスとGroupItems
クラスの定義があり、また
browser/base/content/browser-tabview.js
にタブ及びタブグループ周りの関数が定義されていました。
この2つのファイルを詳しく見ていきます。
browser-sets.incファイルの中にCtrl + Shift + E(グループアイテムビュー)で呼び出される関数が記述されていたため、順に追いかけていきます。
まずbrowser-tabview.js内のTabView_toggle
が呼び出され、そこからTabView_show
、さらにself._window.UI.showTabView
を呼び出しています。
showTabView
関数はbrowser/components/tabview/ui.jsの中に定義されていましたが、ここでTabItems.startHeartbeart
やTabItems.checkHeartbeat
といった同期・非同期処理を管理するような関数が呼ばれており、追いきれなくなりました。
次の手がかりとして**Ctrl + Shift + @やCtrl + Shit + ^**といったタブグループ遷移ショートカットをたどろうとしたのですが、これらのショートカットはbrowser-sets.incの中では定義されておらず、grep
してもそれらしいものが引っかからないためここでしばらく挫折していました。
仕方がないで勘に頼ってbrowser/base/content/browser-tabview.jsをを頭から眺めているとタブグループのショートカットキー**Ctrl + Shift + @やCtrl + Shit + ^**で呼び出される関数が定義されている箇所を奇跡的に発見しました。
// Adds new key commands to the browser, for invoking the Tab Candy UI
// and for switching between groups of tabs when outside of the Tab Candy UI.
_setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
if (this._browserKeyHandlerInitialized)
return;
this._browserKeyHandlerInitialized = true;
let self = this;
window.addEventListener("keypress", function(event) {
if (self.isVisible() || !self._tabBrowserHasHiddenTabs())
return;
let charCode = event.charCode;
// Control (+ Shift) + `
if (event.ctrlKey && !event.metaKey && !event.altKey &&
(charCode == 96 || charCode == 126)) {
event.stopPropagation();
event.preventDefault();
self._initFrame(function() {
let groupItems = self._window.GroupItems;
let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
if (!tabItem)
return;
if (gBrowser.selectedTab.pinned)
groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
else
gBrowser.selectedTab = tabItem.tab;
});
}
}, true);
},
###タブグループ遷移関数 interTabGroupMove
ここからgetNextGroupItemTab
関数をラッピングする形でタブグループ間の遷移を行うオリジナル関数を作成します。TabView__setBrowserKeyHandlers
関数を参考にしています。
interTabGroupMove: function TabView_interTabGroupMove(count, flag)
{
for (var i = 0; i < count; i++) {
let self = this;
self._initFrame(function() {
let groupItems = self._window.GroupItems;
let tabItem = groupItems.getNextGroupItemTab(flag);
if (!tabItem)
return;
if (gBrowser.selectedTab.pinned)
groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
else
gBrowser.selectedTab = tabItem.tab;
});
}
},
TabView_interTabGroupMove(count, flag)
関数は、flag
でタブグループを遷移する方向を、count
で遷移する数を引数としています。GropuItems
をID指定でgetするような関数を見つけられなかったためforループを用いる強引なタブグループ間遷移となってしまいましたが、これで所望の機能の一部分を実現できました。
###左右タブ遷移関数 tabMove
次に左右のタブ間遷移を行うオリジナル関数を作成します。これは、Ctrl+Tabで呼び出される関数がわかっていたので難しいことはありませんでした。
tabMove: function TabView_tabMove(count, flag)
{
let self = this;
self._initFrame(function() {
if(!flag){
gBrowser.tabContainer.advanceSelectedTab(1, true);
} else {
gBrowser.tabContainer.advanceSelectedTab(-1, true);
}
});
},
tabMove(count, flag)
関数はタブをいくつ遷移するのか(count
)とどちら方向に遷移するのか(flag
)を引数にとりTabView_interTabGroupMove(count, flag)
と同じ使い方が出来る様に実装しようと試みました。しかし、上のソースを見たらわかるように、引数count
は使われていません。これは、TabView_interTabGroupMove(count, flag)
と同様にforループを回したところタブ移動が1タブ分しか行われなかったためです。
原因は特定できていませんが、今回の目的においては選んでいるタブが左右に1つ遷移することができたら十分であるためこのままとしました。
##タブの遷移とその画面
###ポップアップ画面を作る
browser.jsを眺めた結果として、中に書き加えた変更はブラウジング中常時適応されそうです。そこでCtrl + カーソルキーでタブ遷移用の画面を呼び出すために以下を追加します。
var inputKeyCode;
document.onkeydown = function (e) {
if(!e.ctrlKey){
return;
}
const code = e.keyCode;
if(code <= 40 && code >= 37){
inputKeyCode = code;
const wid = window.screen.width / 1.8;
const hei = window.screen.height / 1.5;
showModalDialog("original.html", window, "dialogWidth:720px;dialogHeight:480px;maximize:no;minimize:no;status:no");
}
}
これでキーが押されると画面中央にポップアップウィンドウが表示され、そこにoriginal.htmlが読み込まれます。またその時にどの方向のカーソルキーで立ち上げたのかを保持しておきます。showModalDialog
を用いることでポップアップ中の親ウィンドウへの操作を制限しています。
###original.htmlを作成
子ウィンドウに読み込んでもらうhtmlファイルを作成しようとして問題がひとつ発生しました。browser.jsと同じディレクトリにファイルを置いたのですが、ビルドしてもオブジェクトディレクトリに反映されません。ということはビルドされる対象ファイルはどこかで指定されているはずと思い、探してみるとmozilla-release/browser/base/jar.mnがそれらしいです。
content/browser/original.html (content/original.html)
この一行を追加したら、無事オブジェクトディレクトリにもoriginal.htmlが反映されるようになりました。今後も新規ファイルを追加する場合はここをいじれば大丈夫そうです。
###親ウィンドウのデータと制御
ここからはoriginal.htmlに処理を記述していきます。この改造が初javascriptであったので今思うと色々書き直したいところではあります(今だとセミコロン付けない派です)が、当時の流れに沿って行きたいと思います。original.htmlのスクリプトの部分のみ書いていきます。
var pw = window.opener; // parent window for using objects in browser.js
var tabView = pw.TabView;
var groupItems = null;
tabMove(pw.inputKeyCode);
var timer1 = setInterval( function(){
if(tabView.getContentWindow()){
groupItems = tabView.getContentWindow().GroupItems;
document.getElementById("text2").textContent = groupItems;
groupItems.flushAppTabUpdates();
clearInterval(timer1);
setTabData();
}
}, 50);
document.onkeyup = function (e) {
if(!e.ctrlKey){
window.close();
}
}
document.onkeydown = function (e) {
tabMove(e.keyCode);
}
function tabMove(code){
~~~
}
window.opener
でそのウィンドウを開いた親ウィンドウのオブジェクトを取得します。これを通じてbrowser.jsの変数や関数にアクセスすることでメインウィンドウを操作します。
TabView
オブジェクトはbrowser-tabview.jsに定義されていて、タブ周りの情報のほとんどを持ってる感じのやつです。しかしこの中のウィンドウ情報ははじめから格納されているわけではないっぽいので、setInterval
で取得できるまで待ちます。GroupItems
はそのウィンドウの全グループのタブの情報が入ってます。
とりあえずtabMove
というキーに応じてタブを移動する関数(これはbrowser-tabview.jsで作成したTabView
オブジェクトの関数とは異なります)があるとして、それをまずウィンドウをポップアップさせた時の入力で実行します。これは簡単な移動のためにも複数回の入力が必要では煩わしいからです。
またCtrlを離した時にポップアップを消すことで邪魔にならないようにします。
###タブ遷移
tabMove
関数の中身を書いていきます。TabView
オブジェクト内に、同グループ内の左右タブ移動にtabMove
、前後のグループ移動にinterTabGroupMove
を用意したので、これをそれぞれのキーに割り当てます。
moving
関数については次の項目で解説します。これはタブ遷移時の簡単なアニメーション用です。
function tabMove(code){
switch(code){
case 37: // left
tabView.tabMove(1, true);
moving(0, 14);
break;
case 38: // up
tabView.interTabGroupMove(1, false);
moving(10, 0);
break;
case 39: // right
tabView.tabMove(1, false);
moving(0, -14);
break;
case 40: // down
tabView.interTabGroupMove(1, true);
moving(-10, 0);
break;
case 84: // "t"
pw.openUILinkIn(pw.BROWSER_NEW_TAB_URL, "tab");
setTabData();
break;
case 87: // "w"
if(pw.gBrowser.selectedTab !== pw.gBrowser.tabContainer.advanceTakeTabUdon(1, true)){
pw.BrowserCloseTabOrWindow();
setTabData();
} else {
if (groupItems) {
deleteItem = groupItems.getActiveGroupItem();
tabView.interTabGroupMove(1, false);
deleteItem.destroy(true);
setTabData();
}
}
break;
case 82: // "r"
if (groupItems) {
deleteItem = groupItems.getActiveGroupItem();
tabView.interTabGroupMove(1, false);
deleteItem.destroy(true);
setTabData();
}
break;
case 77: //"m"
var uri = window.prompt("URLを入力", "http://www.google.co.jp/");
//document.getElementById("text3").textContent = uri;
if(uri){
pw.openUILinkIn(uri, "tab")
setTabData();
}
break;
case 78: //"n"
if (groupItems) {
groupItems.newGroup().newTab("about:blank", true);
setTabData();
}
break;
case 83: //"s"
var words = window.prompt("google検索", "What do you want to know?");
if(words){
pw.openUILinkIn("http://www.google.co.jp/#q="+words, "tab")
setTabData();
}
break;
default:
pw.console.log("code:" + code);
break;
}
}
function moving(down, right){
~~~
}
このポップアップウィンドウが表示されている間は親ウィンドウのショートカットは無効になっているので、空いたキーにも様々な機能を割り当てています。詳細は割愛しますが、これでポップアップのまま新規タブや新規グループを開いたり、閉じたりできます。新規タブは空タブ、URL直接打ち込み、Google検索の3通りで開けます。
###プレビュー位置の移動
moving
関数の中身を見ていきます。スクリプトの外部も併記します。
かなり汚くて書き直したいところですが、とりあえずプレビュー画像表示位置を動かしていることだけ分かれば大丈夫です。この中で使われているsetTabData
関数が実際に画像を読み込んでいるところです。その解説である次の項でラストです。
<div><img id="center_tab" style="position:absolute;"></div>
<div><img id="up_tab" style="position:absolute;"></div>
<div><img id="dawn_tab" style="position:absolute;"></div>
<div><img id="right_tab" style="position:absolute;"></div>
<div><img id="left_tab" style="position:absolute;"></div>
<div><img id="up_tab2" style="position:absolute;"></div>
<div><img id="down_tab2" style="position:absolute;"></div>
<script type="text/javascript">
var tabC = document.getElementById("center_tab");
var tabU = document.getElementById("up_tab");
var tabD = document.getElementById("dawn_tab");
var tabR = document.getElementById("right_tab");
var tabL = document.getElementById("left_tab");
var tabU2 = document.getElementById("up_tab2");
var tabD2 = document.getElementById("down_tab2");
var tabU_base = null;
var tabD_base = null;
var subwid = innerWidth / 3.3;
var subhei = innerHeight/ 3.5;
var diff = subwid / 25;
tabC.style.width = subwid;
tabC.style.height = subhei;
tabC.style.left = (innerWidth - subwid) / 2;
tabC.style.top = innerHeight / 2.8;
var CLeft = tabC.style.left;
var CTop = tabC.style.top;
tabU.style.width = subwid;
tabU.style.height = subhei;
tabU.style.zIndex = 2;
tabU.style.left = (innerWidth - subwid) / 2;
tabU.style.top = innerHeight / 18;
var ULeft = tabU.style.left;
var UTop = tabU.style.top;
tabU2.style.width = subwid;
tabU2.style.height = subhei;
tabU2.style.zIndex = 1;
tabU2.style.left = (innerWidth - subwid) / 2 + diff;
tabU2.style.top = innerHeight / 18 - diff;
var U2Left = tabU2.style.left;
var U2Top = tabU2.style.top;
tabD.style.width = subwid;
tabD.style.height = subhei;
tabD.style.zIndex = 2;
tabD.style.left = (innerWidth - subwid) / 2;
tabD.style.top = innerHeight / 1.5;
var DLeft = tabD.style.left;
var DTop = tabD.style.top;
tabD2.style.width = subwid;
tabD2.style.height = subhei;
tabD2.style.zIndex = 1;
tabD2.style.left = (innerWidth - subwid) / 2 + diff;
tabD2.style.top = innerHeight / 1.5 -diff;
var D2Left = tabD2.style.left;
var D2Top = tabD2.style.top;
tabR.style.width = subwid;
tabR.style.height = subhei;
tabR.style.left = (innerWidth * 1.5 - subwid) / 2 + subwid/4;
tabR.style.top = innerHeight / 2.8;
var RLeft = tabR.style.left;
var RTop = tabR.style.top;
tabL.style.width = subwid;
tabL.style.height = subhei;
tabL.style.left = (innerWidth * 0.5 - subwid) / 2 - subwid/4;
tabL.style.top = innerHeight / 2.8;
var LLeft = tabL.style.left;
var LTop = tabL.style.top;
function moving(down, right){
if(!(down || right)){
return;
}
var moveTab = function(tab, Left, Top){
var L = parseInt(Left);
var T = parseInt(Top);
var timer = 1;
var id = setInterval(function(){
if(timer === 20){
tab.style.left = L + "px";
tab.style.top = T + "px";
setTabData();
clearInterval(id);
}else {
tab.style.left = (L + timer * right) + "px";
tab.style.top = (T + timer * down) + "px";
timer += 1;
}
}, 15);
};
if( ( pw.gBrowser.selectedTab != pw.gBrowser.tabContainer.advanceTakeTabUdon(1, true) ) || ( down != 0 ) ){
moveTab(tabC, CLeft, CTop);
moveTab(tabR, RLeft, RTop);
moveTab(tabL, LLeft, LTop);
}
if(down != 0){
moveTab(tabU, ULeft, UTop);
moveTab(tabD, DLeft, DTop);
moveTab(tabU2, U2Left, U2Top);
moveTab(tabD2, D2Left, D2Top);
}
}
function setTabData(){
~~~
}
</script>
###プレビュー画像の読み込み
最後の項ですが、ここが一番の山場でした。枠を5つ用意したことからわかるように、現在・同グループ左右・前後グループのタブのプレビュー画像を読み込みます。これがカーソルキーで移動する先のタブに対応します。
タブデータがあればそこからtabPreviews.get
を用いてプレビュー画像を取得できるので、タブデータの取得方法を何とかして探します。
左右タブは既存の関数で取ってこれそうなのが見当たらないのでgBrowser.tabContainer.advanceTakeTabUdon
をmozilla-release/toolkit/content/widgets/tabbox.xmlの適当な位置に追加して、これで取得します。このファイルはbrowser-tabview.jsで使用されているgBrowser.tabContainer.advanceSelectedTab
を追っている時に見つけました。書き方は前後の記述を参考にします。
<method name="_takeNewTabUdon">
<parameter name="aNewTab"/>
<parameter name="aFallbackDir"/>
<parameter name="aWrap"/>
<body>
<![CDATA[
var requestedTab = aNewTab;
while (aNewTab.hidden || aNewTab.disabled || !this._canAdvanceToTab(aNewTab)) {
aNewTab = aFallbackDir == -1 ? aNewTab.previousSibling : aNewTab.nextSibling;
if (!aNewTab && aWrap)
aNewTab = aFallbackDir == -1 ? this.childNodes[this.childNodes.length - 1] :
this.childNodes[0];
if (!aNewTab || aNewTab == requestedTab)
return;
}
var isTabFocused = false;
try {
isTabFocused =
(document.commandDispatcher.focusedElement == this.selectedItem);
return aNewTab;
} catch (e) {}
this.selectedItem = aNewTab;
if (isTabFocused) {
<!-- aNewTab.focus(); -->
return aNewTab;
}
else if (this.getAttribute("setfocus") != "false") {
let selectedPanel = this.tabbox.selectedPanel;
document.commandDispatcher.advanceFocusIntoSubtree(selectedPanel);
// Make sure that the focus doesn't move outside the tabbox
if (this.tabbox) {
try {
let el = document.commandDispatcher.focusedElement;
while (el && el != this.tabbox.tabpanels) {
if (el == this.tabbox || el == selectedPanel)
return;
el = el.parentNode;
}
<!-- aNewTab.focus(); -->
return aNewTab;
} catch(e) {
return aNewTab;
}
}
return aNewTab;
}
return aNewTab;
]]>
</body>
</method>
<method name="advanceTakeTabUdon">
<parameter name="aDir"/>
<parameter name="aWrap"/>
<body>
<![CDATA[
var startTab = this.selectedItem;
var next = startTab[aDir == -1 ? "previousSibling" : "nextSibling"];
if (!next && aWrap) {
next = aDir == -1 ? this.childNodes[this.childNodes.length - 1] :
this.childNodes[0];
}
if (next && next != startTab) {
return this._takeNewTabUdon(next, aDir, aWrap);
}
]]>
</body>
</method>
GroupItems
はmozilla-release/browser/components/tabview/groupitems.jsで定義されてます。これを眺めていくとgetNextGroupItemTab
とかいうそれっぽい物があります。この返り値から直接のタブデータではありませんが、前後タブグループのそれに近そうなものが取得できます。
またタブの状態を見つつ、setInterval
関数を用いて、読み込みができていなければ読み込みを繰り返し、プレビューが存在しない特別なタブには適当な画像を当てはめます。新規で用意した画像ファイルはjar.mnに忘れず書いておきましょう。
function setTabData(){
document.getElementById("text2").textContent = groupItems;
var nowTab = pw.gBrowser.selectedTab
document.getElementById("text1").textContent = nowTab.linkedBrowser.currentURI.spec;
tabToTab(nowTab, tabC);
if(groupItems){
tabU_base = groupItems.getNextGroupItemTab(false);
tabD_base = groupItems.getNextGroupItemTab(true);
if(tabU_base){
tabU2.src = "gray.png";
}else {
tabU2.src = "empty.png";
}
if(tabD_base){
tabD2.src = "gray.png";
}else {
tabD2.src = "empty.png";
}
}else {
tabU2.src = "empty.png";
tabD2.src = "empty.png";
}
baseToTab(tabU_base, tabU);
baseToTab(tabD_base, tabD);
if(nowTab !== pw.gBrowser.tabContainer.advanceTakeTabUdon(1,true)){
var tempR = pw.gBrowser.tabContainer.advanceTakeTabUdon(1, true);
tabToTab(tempR, tabR);
var tempL = pw.gBrowser.tabContainer.advanceTakeTabUdon(-1, true);
tabToTab(tempL, tabL);
}else{
tabR.src = "empty.png";
tabL.src = "empty.png";
}
}
function baseToTab(base, tab){
if(base){
var uri = base.tab.linkedBrowser.currentURI.spec;
if(uri.indexOf("about:") === 0){
if(uri.indexOf("about:newtab") === 0){
tab.src = "newTab.png";
}else if(uri.indexOf("about:blank") === 0){
tab.src = "blank.png";
}else {
tab.src = "aboutRobots-icon.png";
}
}else {
tab.src = pw.tabPreviews.get(base.tab).src;
var timerId = setInterval(function(){
if(tab.src === "chrome://browser/content/undefined"){
tab.src = pw.tabPreviews.get(base.tab).src;
}else{
clearInterval(timerId);
}
}, 50);
}
}else {
tab.src = "empty.png";
}
}
function tabToTab(inTab, outTab){
if(inTab){
var uri = inTab.linkedBrowser.currentURI.spec;
if(uri.indexOf("about:") === 0){
if(uri.indexOf("about:newtab") === 0){
outTab.src = "newTab.png";
}else if(uri.indexOf("about:blank") === 0){
outTab.src = "blank.png";
}else {
outTab.src = "aboutRobots-icon.png";
}
}else {
outTab.src = pw.tabPreviews.get(inTab).src;
var timerId = setInterval(function(){
if(outTab.src === "chrome://browser/content/undefined"){
outTab.src = pw.tabPreviews.get(inTab).src;
}else{
clearInterval(timerId);
}
}, 50);
}
}
}
専用ウィンドウとしてこんな感じのポップアップが出るようになりました。画像ではわかりませんが、Ctrlを押したままカーソルキーを上下左右にすることで自由自在なタブ遷移ができます。また遷移のたびにプレビューも更新されます。
##改善点
- ポップアップ画面、アニメーションがかっこわるい
- D3などのライブラリを使ってもう少しスマートに
- ポップアップで親ウィンドウのほとんどが隠れてしまう
- ウィンドウにこだわることなく、何らかの形でポップアップし、透明度も設定できるように
- プレビュー画像の読み込みが不安定
- firefoxの既存機能の内には安定してプレビュー画像が取れているものがあるので、プレビュー取得関数周りを変更
- 新規タブグループを作成した際、強制的にタブグループインデックスの末尾に追加される
- 任意のインデックスに新規タブグループを挿入できるように
##変更を加えたファイル
mozilla-releaseをトップディレクトリとして
- .mozconfig
- browser/
- base/
- content/
- browser.js
- browser-tabview.js
- original.html (new)
- その他画像 (new)
- jar.mn
- content/
- base/
- toolkit/content/widgets/tabbox.xml