Chrome extension的manifest_version升級過程幾個棘手問題的解決方法

之前基於Jquery mobile做了一個Chrome app,就在要給客戶做showcase的時候,chrome強制升級manifest_version到2(http://developer.chrome.com/extensions/manifestVersion.html),而這個version基於安全考慮做了一些限制,導致我的chrome app無法運行,具體限制可見:http://developer.chrome.com/extensions/contentSecurityPolicy.html。對於我的app主要有以下兩個影響點:

  • 限制dom元素上直接onclick
  • 限制new Function

這兩點對於我來說比較棘手。

1)去onclick

由於之前寫js不規範,爲了傳參數方便,也由於存在動態創建dom的需求,所以就直接onclick,整個app有多達十幾個onclick,比如:

var item = "<li data-theme=\"c\"><a data-transition=\"slide\" οnclick=\"loadViewList('"+data.data[i]+"')\">"+data.data[i]+"</a></li>";
$(item).insertAfter($("#projectListTitle"));

這裏給我出的難題是這些dom是在某種場景下創建出來的dom,很難在統一的做事件偵聽,再比如:

<li><a id='loadAssignToButton' οnclick='loadAssignTo(${id})'>Assign CR</a></li>
這裏的難題是這段代碼是模版裏的,參數是在模版渲染時才能確定的,很難在js事件偵聽裏傳參。並且以上問題在原有代碼裏存在很多,需要有個一勞永逸的方法來解決。

幸好,jquery很強大,一方面它提供了很好的selector,能讓我很方便的一處監聽所有需要處理的dom,另一方面,它有一個delegate的機制,也就是說除了能直接在dom上進行監聽外,還能讓這個dom監視它的子dom,能實現當子dom創建時再監聽事件的功能,具體可見jquery.on api(http://api.jquery.com/on/)。於是,我給出了一個一勞永逸的方法:

$(document).ready(function() {
    $("div[data-role='page']").on("click", "a", function(event) {
        if($(this).attr("cf")){
            var funName = $(this).attr("cf");
            if(funName.indexOf(".") == -1){
                window[funName]($(this).attr('cp1'),$(this).attr('cp2'));
            }else{
                var funNames = funName.split(".");
                window[funNames[0]][funNames[1]]($(this).attr('cp1'),$(this).attr('cp2'));
            }
        }
    });
});

然後,改寫所有用到onclick的點,比如:

<li><a id='loadAssignToButton' cf='loadAssignTo' cp1='${id}'>Assign CR</a></li>

其中,cf,cp1和cp2分別對應着onclick監聽處理函數名和參數名,是自定義屬性,只是爲了傳參,當然也有更優雅的方式,不過這不是這裏的重點。

2)去new Function

如果只是簡單的使用new Function還好辦,不過我這裏不是直接使用了new Function,而是引用的jquery template裏面用到了new Function,並且這是做模版的核心(通過動態function來渲染模版),具體用到的地方如下所示:

// Generate a reusable function that will serve to render a template against data
function buildTmplFn( markup ) {
	return new Function("jQuery","$item",
......
)}
解決這個問題要麼是換模版方案,要麼就是使用chrome提供的sandbox特性,由於應用模版的地方較多,換模版的成本太大,所以只好看看這個sandbox能否解決我的問題。使用方法可見:http://developer.chrome.com/trunk/apps/manifest.html#sandbox

於是乎,我照葫蘆畫瓢通過sandbox來改寫使用了模版的地方。

1. 首先新建sandbox.html,把模版挪過來

2. 在sandbox.html,添加模版渲染的請求偵聽:

	<script>
      // Set up message event handler:
      window.addEventListener('message', function(event) {
      	var data = event.data.data;
      	var tmpl = event.data.tmpl;
        var dataRendered = $( "#"+tmpl ).tmpl( data ).wrap('<div>').parent().html();
        var result = {'data':data,'dataRendered':dataRendered,'tmpl':tmpl};
        event.source.postMessage({'result': result}, event.origin);
      });
    </script>

其中$( "#"+tmpl ).tmpl( data ).wrap('<div>').parent().html()是由於$( "#"+tmpl ).tmpl( data )返回的是jquery的dom,sandbox是基於HTML5 web worker來做的,它並不是同享內存,所以不能傳遞dom,這裏只好先把dom轉爲html,然後在主頁面裏通過html創建dom。

3. 在主頁面裏添加隱藏的iframe來向sandbox發請求

<iframe id="theFrame" src="sandbox.html" style="display: none;"></iframe>
var message = {'data':data,'tmpl':'crFilterTemplate'};
document.getElementById('theFrame').contentWindow.postMessage(message, '*'); 
4. 在主頁面監聽message,取得模版渲染後html,並創建dom

window.addEventListener('message', function(event) {
    var data = event.data.result.data;
    var dataRendered = event.data.result.dataRendered;
    var tmpl = event.data.result.tmpl;
    window[tmpl+'Render'](data, dataRendered);
});

function crDetailTemplateRender(data, dataRendered) {
    $(dataRendered).appendTo(document.body);
    $("#crDetailPage").attr( "data-" + $.mobile.ns + "external-page", true ).one( 'pagecreate', $.mobile._bindPageRemove );
    $.mobile.changePage( "#crDetailPage", { transition: "flip"} );
}

升級manifest_version本來沒在工作計劃之內,是突然冒出的攔路虎,不過也給自己提了個醒,一旦基於某個平臺上做事情,一定得多關注這個平臺的遊戲規則,不然陷阱重重啊
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章