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本来没在工作计划之内,是突然冒出的拦路虎,不过也给自己提了个醒,一旦基于某个平台上做事情,一定得多关注这个平台的游戏规则,不然陷阱重重啊
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章