聚會時間公告: 因應COSCUP 2011, Kalug 8月份休會一次

九月 3, 2013

Yuren's Info Area
yurinfore
is about »

tag cloud

» Customize Your Firefox OS

禮拜六在風雨交加下到了高雄的 KSDG 講了 "Customize Your Firefox OS",從 Firefox OS 介紹、內建客制化能力以及如何透過修改源碼更深度的客制化。台灣有許多 OEM 廠商透過不同的 OS 來建制自己的解決方案,希望這個分享可以讓大家可以瞭解到如何從內建的客制化機制以及修改源碼來建制自己的 Firefox OS。


七月 15, 2013

Yuren's Info Area
yurinfore
is about »

tag cloud

» Sinon.JS - unit test 斬斷相依性的利器

這兩天跟上周抽出一些時間正在弄 Firefox OS 中 system app 的 unit test,然後這次好好讀了 sinon.js 之後開始試著用他來處理一些相依性問題感覺還真不賴。

是這樣的,unit test 主要的目的是檢測特定的 unit 的工作是否正常運作,在這樣的狀況下我們僅測試該 unit 的邏輯與功能,至於跟它相依的部分通常會在另外一個 unit test 或是 integration test 的時候再測試。

但是問題來了,我想測試的 function 就是有用到其他外部 Object,你總不可能要我整個 Javascript 都沒用到 document.getElementById 吧(這還是有可能啦...)?

Sinon.JS 的其中一個功能就是可以隔絕 unit 對於其他 Object 的相依性,讓開發者可以單獨測試單一 unit 邏輯的好幫手。剛好我最近正在寫 system app 其中 ScreenManager 的 unit test,讓我們來看看 ScreenManager.init() 這個函式的相依性關係:



ScreenManager.init() 除了 call 外部的五個 Object 以外,還調用了自己的三個 function。而這些相依性都可以透過 Sinon 的功能隔絕他們。

在這之前先介紹一下 Sinon.JS 的三大物件:

Spy: 可以把一個 object/function wrap 起來,可以用來監看該 object/function 被呼叫的狀況。舉個例來說,假設我們要測試某個條件下 init() 就會呼叫到 turnScreenOn(), 我們可以用 spy 把 turnScreenOn wrap 起來:

var spyTurnScreenOn = sinon.spy(ScreenManager, 'turnScreenOn');
ScreenManager.init();
assert.isTrue(spyTurnScreenOn.called);

上面這段的意思是如果執行了 init() 之後,ScreenManager.turnScreenOn 會被執行到就代表正確。如果不僅執行到,而且傳入的參數一定要是特定值(比如說 true)也可以這樣用:

spyTurnScreen.calledWith(true);

除了上面提到這兩個 API 以外,還有幾個都還蠻實用的如 calledCount, calledTwice 等等,詳情請見 Spy API

Stub: 有 spy 的所有功能。但是造出 stub 之後,原本的 function 就不會再被呼叫了。我通常都會用 stub 把所有的相依性全部切掉。一樣是 turnScreenOn() 在 init() 會被呼叫的例子,如果使用 stub 代替 spy,不一樣的地方是 spy 還是會去呼叫原有 function,而 stub 則不會呼叫原有 function。

Mock: 有 Spy 跟 Stub 所有功能,但是還可以透過在跑之前設定期望值,最後再檢查 Mock 後的物件是否有照期待的執行。舉個 Sinon.JS 官網上面的例子,下面這樣的寫法可以讓你確認 jquery.ajax 最少被呼叫到了兩次,最多呼叫五次:

sinon.mock(jQuery).expects("ajax").atLeast(2).atMost(5);
...
jQuery.ajax.verify();

跟 Spy, Stub 不一樣的是 Mock 可以在測試還沒開始前就先預先指定期望值,而不是像 Stub 一樣需要等到呼叫後才能檢測條件是否成立。不過我目前測試的部分都可以用 stub 做到,Mock 暫時還沒用到,等到真的有用到之後再跟大家分享。

接下來我們先來看一下相對簡單的例子要怎麼測試:ScreenManager:toggleScreen():

toggleScreen: function scm_toggleScreen() {
if (this.screenEnabled) {
// Currently there is no one used toggleScreen, so just set reason as
// toggle. If it is used by someone in the future, we can rename it.
this._screenOffBy = 'toggle';
this.turnScreenOff();
} else {
this.turnScreenOn();
}

這個 function相當的簡單,只要 screenEnabled 是 true,_screenOffBy 就會是 toggle,並且 turnScreenOff 會呼叫到。所以只要利用 sinon.stub 分別作出 turnScreenOff 跟 turnScreenOn 的 stub,並且確認會不會正確的呼叫到即可。

suite('toggleScreen()', function() {
var stubTurnOff, stubTurnOn;

setup(function() {
stubTurnOff = sinon.stub(ScreenManager, 'turnScreenOff');
stubTurnOn = sinon.stub(ScreenManager, 'turnScreenOn');
});

teardown(function() {
stubTurnOff.restore();
stubTurnOn.restore();
});

test('if screenEnabled is true', function() {
ScreenManager.screenEnabled = true;
ScreenManager.toggleScreen();
assert.equal(ScreenManager._screenOffBy, 'toggle');
assert.isTrue(stubTurnOff.called);
assert.isFalse(stubTurnOn.called);
});

test('if screenEnabled is false', function() {
ScreenManager.screenEnabled = false;
ScreenManager.toggleScreen();
assert.isTrue(stubTurnOn.called);
assert.isFalse(stubTurnOff.called);
});
});

所以我在 5-6 行的地方用 sinon.stub() 分別對兩個 function 做了 stub,這樣一來當 toggleScreen() 呼叫這兩個 function 的時候,就會呼叫到假的 function 了。比如說 15-19 行的測試,當 screenEnabled 是 true 時,turnOff 要被呼叫到,而 turnOn 則不會被呼叫到。sinon.stub() 在這種種狀況就可以協助我們把相依性切除,只專注在測試目標 function 的邏輯。

但是我們的測試環境是將 Firefox OS 在瀏覽器上面運行,這時候有些 Object 很可能是不存在的。比如說 mozTelephony,所以就不能用上面 sinon.stub(Object, propertyName) 的方式造假物件,還是要利用 sinon.stub() 造一個新的物件。正是因為這樣我設計了 switchProperty/restoreProperty 的 helper function 用來替換掉可能不存在的物件。

再來看看一些比較進階的例子:如果你有一個 function 只希望在某種狀況下,才使用 stub 指定的回傳值,而其他狀況則是調用原本的 function 該怎麼寫呢?init() 的 setup() 正好有這樣的狀況:

var stubById = sinon.stub(document, 'getElementById').withArgs('screen')
.returns(document.createElement('div'));

這段的意思是我們為 document.getElementById 做了一個假物件,他會直接回傳一個 div DOM 元件,但是只有在傳入參數為 screen 的時候才會發生,所以說:

document.getElementById('screen');

這個時候就會直接回傳一個假的 div,而不會真的到 DOM 裡面查詢一個 #screen 的元素,而且如果你查的是任何其他的參數,就還是可以正常運作!這樣可以確保你的 stub 只在你想要的參數被觸發!

還有一個很常需要對付的狀況:如果你的測項是需要被 callback 呼叫才能測該怎麼辦呢?舉例:你用了一個 addEventListener('click', callback),所以說一定要 click 才能呼叫 callback 該怎麼辦呢?用下面的 function:

sinon.stub(window, 'addEventListener').callsArgWith(1, evt)

如果一呼叫 addEventListener 就會直接呼叫第一個參數('click' 是 0, callback 是 1),這樣就可以測試更深層的狀況了。

總而言之,Sinon.JS 準備了一拖拉庫的造假工具給你用,Javascript Unit Test 有了像 Sinon.JS 這樣強大的工具,就可以切斷相依性,測試的更徹底!

另外這邊有一個 Firefox OS 裡面的範例:screen_manager.jsscreen_manager_test.js,有興趣的可以參考參考。

七月 6, 2013

Yuren's Info Area
yurinfore
is about »

tag cloud

» Running Firefox OS Gaia on Windows


你永遠不知道下一個要解的 Bug 是什麼 :-)

上週送了一個 Pull Request 到 Gaia,Reviewer 非常好心的跟我說我的 patch 在 Windows 上面不會動,這時候才第二次意識到我們還是需要在 Windows 上面測試(上一次是我修 customization 的時候遇到的)。總之我這次很老實地把 Windows 的環境搞定了,在我等待 fetch pull request 的時候就來說說怎麼設定吧。

首先下載 Firefox Nightly

接著你需要 MinGW,寫這篇文章的時候我裝的是 mingw-get-inst-20120426.exe,或許你看到這篇文章的時候已經有新版出來了。安裝的時候記得要選下面這兩個:
  • MSYS Basic System
  • MinGW Developer ToolKit(這個我不確定要不要)
接下來安裝 git,Git 直接到官方網站下載就好了。安裝的時候選擇 "Run Git from the Windows Command Prompt",這樣可以直接在 MinGW 的環境使用 git。

然後接下來是裝 Python,一樣到官網下載就好了。 但是你需要把 Pyhton 的路徑加入 PATH 這個環境變數裡面,在 My Computer 按右鍵,選 properties -> Advanced -> Environment Variables,找到 Path 把 C:\Python27 加入。

最後的一個步驟,安裝一些 build gaia 會用到的指令:

$ mingw-get install msys-wget
$ mingw-get install msys-zip
$ mingw-get install msys-unzip

這樣就搞定啦!

打開 MinGW Shell 找個風水吉地 git clone gaia,進入目錄後用下面指令 build 出 profile 檔:

$ DEBUG=1 make

第一次會需要久一點,主要是下載一些 xulrunner 之類的東西。好了之後用下面的指令啓動你的 Nightly。依據你 clone Gaia 的地方會有些不一樣,自己摸索一下吧。

$ /C/Program\ Files/Nightly/firefox.exe -profile /C/MinGW/msys/1.0/home/IEUser/gaia/profile-debug/

這樣就可以在 Windows 的 Nightly 上面啓動 Gaia 囉,在裡面收信也沒問題喔 LOL


七月 4, 2013

Yuren's Info Area
yurinfore
is about »

tag cloud

» Gaia Development Workflow

這是 Firefox OS - Gaia 開發時的 workflow,沒時間寫文章,先放張圖。


一月 22, 2013

小惡魔AppleBOY
AppleBOY
is about »

tag cloud

» Firefox OS Developer Preview Phone 簡介

firefox-phone

上次提到今年 Firefox 將會舉辦 2013 Firefox OS App Days,這次看到 mozilla hacks 提到一篇: Announcing the Firefox OS Developer Preview Phone!,Firefox OS 是新一代的作業系統,跟 Android 或 IOS 比較不同的地方就是,你可以用 HTML5 可以做到手機的任何事情,包括讓手機振動,傳簡訊,或者是撥打電話,我想這是 iOS 或者是 Android 是無法做到的,該篇還提到 Firefox Phone 硬體規格:

CPU Qualcomm Snapdragon S1 1Ghz
UMTS 2100/1900/900 (3G HSPA)
GSM 850/900/1800/1900 (2G EDGE)
Screen 3.5″ HVGA Multitouch
3 MP Camera
4GB ROM, 512 MB RAM
MicroSD, Wifi N, Light and proxmity Sensor, G-Sensor, GPS, MicroUSB
1580 mAh battery
Over the air updates
Unlocked, add your own SIM card

如果想開始設計 App for Firefox OS 可以參考這篇,另外文章有提到如果你要試試看 Firefox OS + 自己開發的 App,可以透過底下方式:

1. 安裝 Marketplace for Android 在您的 Android 手機上。
2. 安裝瀏覽器版本 Firefox OS 模擬器
3. 安裝 Firefox OS 到各家硬體上
4. 最後一種方法:那就是買支 Firefox OS Developer Preview device

如果想更了解 Firefox OS 的話或有興趣的可以自行報名參加台北 Mozilla 1/26 場次

Related View


Yuren's Info Area
yurinfore
is about »

tag cloud

» 威能的 Firefox OS unit testing

今天在謀智台客發表了篇文章,主要是講 Firefox OS 在 unit testing 有個不錯的機制,就是設定妥當後,當你在任何編輯器或 IDE 按下儲存後,unit testing 就會自動開始測試跟你剛剛儲存的那個 javascript 相關的測項,最後用 Mac 的 notification center 或是 Linux 的 libnotify 告訴你測試結果,像下面這樣:



可以讓你隨時都知道自己有沒有把任何東西搞爆了。

有興趣的可以看一下

缺它不可!靈活運用 Firefox OS Gaia 的單元測試

十一月 27, 2012

Yuren's Info Area
yurinfore
is about »

tag cloud

» [Firefox OS] HTML 內嵌 SVG 動畫實例

前言:這些實作都已經包含在最新的 Firefox OS 裡面,有興趣的可以 check 最新的 code 出來玩。

最近接到了一個需要變更設計的 issue,我看到這個設計愣了一下,主要的原因是因為要做出這樣的設計用 HTML + CSS 還真的需要想一下如何實作。

主要更改的地方是 Firefox OS 的兩個元件:Lockscreen 跟 Dialer。

這兩個元件主要的設計概念都是希望有一條像是橡皮筋(或跳繩)的線上下的跳動,並且在往上跳動的時候露出下面的兩個按鈕,提示使用者可以把這條橡皮筋往上滑,接下來按下按鈕解鎖。我們這邊只討論如何實作,不討論視覺設計 :P



如上圖所示,上面的那條弧線是需要動態的上下彈動,如果用 HTML + CSS 的話有幾種方法可以嘗試做到跟缺點:

  1. 上一個 canvas,然後把這條線在 canvas 上面不斷的重繪(缺點:這樣要不停的計算跟重繪)
  2. 用超級多張 png 不停的置換圖檔(缺點:要生出超級多圖)
  3. 先畫一張弧線,接著用 CSS 的 Transform 更改他的 scale()(缺點,接近中間的時候整個圖形就會被壓得很扁)
考慮過 HTML + CSS 的解法之後,以上的解法似乎都不太好。這個時候我就開始考慮用 SVG 來做這件事情。但是用 SVG 來做這件事情其實是蠻冒險的,因為在這期間問了幾個同事他們都沒有測試過 SVG 在 Firefox OS 真正的手機上的詳細效能。所以收到這個設計的時候我先寫信問了個對於整個 platform 比較熟的同事,然後因為 deadline 非常的趕,但是我又不能不確定效能狀況就下手,所以就先決定做個獨立可以同時在電腦跟手機都可以驗證的小型 app。

這個 app 要驗證的事情有兩件:
  1. 當用滑鼠(手指)按住拖曳,這個時候改變 SVG 的屬性讓他改變弧度的效能衝擊有多大
  2. 使用 SVG animation (SMIL) 效能到底如何
然而做這個 DEMO 幾乎也可以搞清楚要怎麼用 SVG 實作這個解鎖畫面了。在弧線的部分,採用 SVG 的 Path 搭配上 c (curveto) 參數可以達成弧線,動畫的部分則是用 SMIL 的 animate tag 完成。下面這個網頁就是驗證效能用的網頁(我只用過 Firefox 打開過,其他瀏覽器不知道有沒有支援):

http://yurenju.github.com/lockscreen-demo/wrapper.html

這邊只是用來驗證的網頁,所以會有一些小 bug。主要的功能就是往上拉的時候用 javascript 去改變 SVG d (data) 裡面的 c (curveto) 的參數,放開滑鼠的時候用 SMIL animate 把 curve 滑回原位。而下面有個連結 install lockscreen demo 用途是如果你用 Firefox OS 的手機點了這個連結就可以把這個 demo app 安裝到手機裡面。

很棒的是當我把這個 app 安裝到手機裡面,發現這樣實作的效能在手機上是完全可以接受的!既然可以接受那就大膽的把這樣的實作方式引入 Firefox OS。這邊有針對 lockscreen 的 commit


在 SVG 方面,首先利用 path tag 來劃出最原始的弧線。在 attribute d (data) 裡面用了兩個參數:M (moveto) 跟 C (curveto),moveto 用來指定 path 的起點,curveto 用來指定用來控制曲線的兩根桿子的弧度。SVG 1.1 Path 裡面有張圖讓 curve 的控制點比較好理解一點:



C 後面接的兩組 (x, y) 分別是兩個控制點的坐標,我們需要的大概像是最左上角那張圖的效果。attribute 'd' 裡面的最後一個參數則是曲線的終點。整個 attribute 長這樣:

M0,80 C100,150 220,150 320,80

第一個是曲線起始坐標,最後一個是曲線結束坐標,中間兩個則是控制點的坐標。

接下來說明 path 裡面的五個 animate tag。1 跟 3 是針對曲線的彎度變化,而 2 跟 4 則是針對透明度的變化。1-4 都是用於拖曳橡皮筋之後放開的動畫,而 (5) 則是當你不去碰橡皮筋時,他的彈跳提示動畫。請想像這一整組動畫:一條繩子彈上去後隨著重力掉下來,掉到地上之後會再彈跳幾下後靜止。下面講解比較複雜的第五組動畫:

先看到 values。用分號切分開來的話總共有五組數據:
  1. 起始的曲線數據 (Y=150)
  2. 第一次彈跳到最高的數據,兩個控制點的 Y 坐標都變少了讓整個圓弧的開口朝下 (Y=40)
  3. 回到最地上 (Y=150)
  4. 再次彈起來,但是幅度較低 (Y=100)
  5. 回到原點 (Y=150)
至於 keySplines 則是指定彈跳的 timing function,如果你用過 CSS animation,就跟 ease-in/out 那種差不多的東西,只是要直接指定數據,下面這張圖是 keySplines 設定 0.5 0 0.5 1 會產生的 timing function:


keySplines 裡面有四組數據,分別就是 1-2, 2-3, 3-4, 4-5 這四段動畫的 timing function。每一組數據裡面都是兩個控制點的坐標。

SVG 的部分大概就是這樣!這部分有很多需要細微調整的,有興趣的就留言一起討論吧。接下來是 Javascript 部分。這邊我就講一些 SVG + Javascript 要注意的小技巧
  • fill=freeze 功能為讓動畫結束之後停留在最後一格,不過這樣的話如果你想要用 mousemove 去逐漸改變曲線的外形時,你會發現這個屬性會讓整個 path 卡死。如果拿掉 fill=freeze 的話,因為我執行完動畫之後還要把曲線固定在最後一格,所以拿掉的話就導致動畫有閃爍的現象。 解法就是平常不用,等到要播放補間動畫的時候再把 freeze 加上去。
  • 用 beginElement(), endElement() 來播放、停止動畫
  • addEventListener endEvent 來處理動畫結束後的後續處理。
這邊的細節真的非常的多,如果你也想 HTML + SVG + Javascript 來實作的話,建議是要讀一下 SVG 跟 SMIL 的 spec,然後撿想要用的東西放在裡面,什麼不明白的事情就直接寫到 SVG 裡面看一下效果如何就是了。那個時候這個 commit 要上真是超級緊張的,因為這是我開始 contribute Firefox OS 以來最大的修改。結果上的時候還是有些小細節沒注意到,感謝同事的幫忙在 bug 還沒關之前就注意到這個低級錯誤然後讓我可以及時的推入 repository 了。

之後更複雜的是 Dialer 的部分。



如上圖所見左右兩邊各有一個會隨著弧線移動的兩個 spotlight,而線段上的顏色還多了紅色跟綠色線段。線段不同顏色方面,看遍了 SVG 的資料後比較方便的方法還是用多重的漸層並且把兩個漸層的 offset 設定成一樣,這樣就可以讓曲線有不同的顏色。至於隨著 curve 的 spotlight 則是透過 clipPath 作修剪遮罩,讓漸層只在部分的地方露出來即可。做完這次的 commit 我的 SVG 功力真的大增啊... Orz



這是在 Dialer 曲線用的漸層。2 跟 3 的 offset 都是一樣的,但是顏色卻用不一樣,這樣的技巧可以讓線段不會產生漸層。

這段是如何產生 spotlight 的方法。首先 path 不一樣的地方是 d 除了原本的 M 跟 C 以外,又加了 H V Z 分別用來畫出橫線、直線跟關閉 path 用。fill style 則套用上面的 #gradient-red 的漸層紅色,最後用 clipPath 的方式作剪裁遮罩。對綠色的部分也用相同的方法,最後通通拿去做動畫,就完成啦!

所以這是最後的結果:



Dialer 最後實作的結果在這邊,很可惜速度上並不是很好,目前看起來撥電話進來之後整隻手機的效率會下降許多,目前我們也正在改進這個問題。



這個 lockscreen 的 code 都在 github 上面,有興趣的可以抓下來玩玩 :-)

    十月 17, 2012

    Yuren's Info Area
    yurinfore
    is about »

    tag cloud

    » jscallgraph - Javascript Call Graph 靜態分析

    最近用 xmind 追 code 追得很辛苦,跟幾位朋友討論過後,不知不覺就開始寫起了靜態分析用的程式。昨天先用 python 驗證了一下概念,深夜開完會議之後就決定用 node.js 來寫個程式會比起用 python 更為合適。目前已經放上 npm,要安裝只要你已經裝好了 node.js 跟 npm,輸入以下指令就可以安裝了。

    $ sudo npm -g install jscallgraph
    $ sudo apt-get install graphviz

    第二行是安裝 graphviz,主要的用途是拿來產生分析完的圖形用的。使用上很簡單,舉例來說你想要對 Firefox OS 的 system app 裡面的 window_manager.js 作靜態分析,把檔案抓下來,用以下指令產生 dot file

    $ jcg window_manager.js WindowManager > window_manager.dot

    第一個參數是要分析的檔案,第二個參數是要分析的物件,這邊可以參考一下 window_manager.js 的檔案,裡面宣告了 WindowManager 物件,並且把相關 method 都包裝在裡面。

    接下來用 graphviz 的 dot 指令產生 png 圖檔

    $ dot -Tpng -o window_manager.png window_manager.dot


    這樣就完成了!



    Source code 放在 github, 請大家自由取用!另外這程式不是什麼 javascript 都可以分析,有興趣的請送 patch! :D

    十月 15, 2012

    Yuren's Info Area
    yurinfore
    is about »

    tag cloud

    » [Firefox OS] 呼叫 MozActivity 的內部訊息流程

    上週快結束的時候我一直在追蹤一個 Firefox OS 的 Bug #800169,後來發現不是 Gaia (Firefox OS 的 App 層) 之後,我就一路往下看到了 Gecko (Firefox OS 的 Runtime 層),當我覺得快要找出癥結的時候,這個 Bug 在 Nightly build 被別人解決了! XDD

    不過趁著這個機會也把架構熟悉過了一遍,跟大家分享一下。

    先解釋一下這個 bug,Firefox OS 的瀏覽器在把一個網頁加入到 home screen 的時候,加入 Home screen 的確認視窗會跳出來兩次。

    首先我們就從 Browser 的 browser.js 開始,按下『加入至 home screen』後會使用下面的 API 呼叫加入 home screen 的 dialog 。

    new MozActivity({
    name: 'save-bookmark',
    data: {
    type: 'url',
    url: this.currentTab.url,
    name: this.currentTab.title,
    icon: place.iconUri
    }
    });

    MozActivity 是怎麼呼叫 Dialog 的呢?經過追蹤,當你呼叫了 MozActivity 的時候,真正執行的是 B2G/gecko/dom/activities/src/Activity.cpp。當你找出這個地方後可以用 gdb 來確認是不是這裡,詳細的用法可以參考 Debugging B2G using gdb。Activity:Initialize 的最後面的程式碼是這樣:

    nsresult rv;
    mProxy = do_CreateInstance("@mozilla.org/dom/activities/proxy;1", &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    mProxy->StartActivity(this, options, window);
    return NS_OK;

    事實上這個時候 B2G 去調用了同一個目錄底下的 ActivityProxy.js,這個時候用 Child Process Message Manager 的  sendAsyncMessage 丟了用來開啟 Activity 的訊息出去。

    cpmm.sendAsyncMessage("Activity:Start", { id: this.id, options: aOptions });
    cpmm.addMessageListener("Activity:FireSuccess", this);
    cpmm.addMessageListener("Activity:FireError", this);

    ActivityService.jsm 的 receiveMessage 會接收到這個訊息,並且交由 this.startActivity 來處理之。而 startActivity 決定完成要用哪個 app 開啟這個 Activity 後,再用 system-message-internal 的 sendMessage 丟出一個名為 activity 的訊息。

    let sysmm = Cc["@mozilla.org/system-message-internal;1"]
    .getService(Ci.nsISystemMessagesInternal);
    if (!sysmm) {
    // System message is not present, what should we do?
    return;
    }

    debug("Sending system message...");
    let result = aResults.options[aChoice];
    sysmm.sendMessage("activity", {
    "id": aMsg.id,
    "payload": aMsg.options,
    "target": result.description
    },
    Services.io.newURI(result.description.href, null, null),
    Services.io.newURI(result.manifest, null, null));

    最後到了 SystemMessageInternal.js 裡面的 sendMessage 最後會調用 _processPage 來開啟正確的 App。

    let page = { uri: aPage.uri,
    manifest: aPage.manifest,
    type: aPage.type,
    target: aMessage.target };
    debug("Asking to open " + JSON.stringify(page));
    Services.obs.notifyObservers(this, "system-messages-open-app", JSON.stringify(page));

    而追蹤的 bug 的問題點就在這裡,這邊有兩個 match 的 page,所以他連續開啟了兩次 add to home screen 的 dialog。當我追蹤到這邊的時候,其實基本上已經找到問題的根源了。不過在跟別人討論的過程中突然發現有另外一個 bug 的 patch 已經解決這個問題,而且在 nightly 的 build 也不會有這個問題,所以我就沒繼續追蹤下去了。

    藉由這次機會也從 Gaia 到 Gecko 看了一大圈,也算是很有收穫  :D

    support:

    biggo.com.tw

    biggo.sg

    A Django site.