

在几周前的《Google gadget engine对HTML/CSS的支持分析》一文中最终没有确定engine中的哪一块儿进行了HTML/CSS解析,只是怀疑libxml2。这两天继续分析了这个问题,发现libxml2确实支持HTML解析,但在engine中进行HTML/CSS解析的却不是libxml2。
定位HTML/CSS解析
上回提到有关HTML/CSS的显示只能在pluginHelper这个插件窗口中进行(采用pluginHelper.showDetailsView函数),而不能在gadget的主窗口中实现。所以,可以说Google gadget自身并不支持HTML/CSS的解析,而只能通pluginHelper实现。也就是说gadget界面部分只能通过view对象实现,不支持HTML/CSS的实现,而内容方面则可借用pluginHelper进行HTML/CSS的显示。下面我们就分析一下showDetailsView(Gadget.cc中)这个函数。其中有如下一段:
if (details_view_data->GetContentIsHTML() ||
!details_view_data->GetContentIsView()) {
if (details_view_data->GetContentIsHTML()) {
xml_file = kHTMLDetailsView;
ScriptableInterface *ext_obj = details_view_data->GetExternalObject();
context->AssignFromNative(NULL, “”, “external”, Variant(ext_obj));
data->PutValue(”contentType”, Variant(”text/html”));
} else {
xml_file = kTextDetailsView;
data->PutValue(”contentType”, Variant(”text/plain”));
}
data->PutValue(”content”, Variant(details_view_data->GetText()));
GetGlobalFileManager()->ReadFile(xml_file.c_str(), &xml);
} else {
xml_file = details_view_data->GetText();
file_manager_->ReadFile(xml_file.c_str(), &xml);
}
这段代码比较迷惑人,容易让人误认为 GetGlobalFileManager() ->ReadFile(xml_file.c_str(), &xml); 是在读入HTML内容并统一名称用libxml2进行解析,实际上这段代码的重点却在xml_file = kHTMLDetailsView; 这里。查找kHTMLDetailsView的定义,在Gadget_consts.h中:
const char kHTMLDetailsView[] = “resource://html_details_view.xml”;
可见,GetGlobalFileManager()->ReadFile(xml_file.c_str(), &xml); 读入的是一个engine自带的固定文件,而非HTML内容。resource://html_details_view.xml文件的内容如下所示:
<?xml version=”1.0″ encoding=”UTF-8″?>
<view width=”300″ height=”250″ resizable=”true” onopen=”onopen()”
onsize=”onsize()”>
<script>
<!–
function onopen() {
browser.contentType = detailsViewData.getValue(”contentType”);
browser.innerText = detailsViewData.getValue(”content”);
browser.onGetProperty = function(name) {
if (external[name] == undefined) return “\”undefined\”";
if (typeof external[name] == “function”) return “\”function\”";
return external[name];
};
browser.onSetProperty = function(name, value) {
external[name] = value;
};
browser.onCallback = function(name, args) {
external[name].apply(null, args);
};
}
function onsize() {
browser.width = view.width - 2;
browser.height = view.height - 2;
}
–>
</script>
<!– a background acts as input shape mask. –>
<div width=”100%” height=”100%” background=”#FFFFFF”/>
<_browser name=”browser” x=”1″ y=”1″ width=”298″ height=”248″/>
</view>
这里有明显的调用浏览器过程,结合第一段代码中ScriptableInterface *ext_obj = details_view_data->GetExternalObject(); 语句在原码中发现相关内容,在extensions\gtkmoz_browser_element中的browser_child.cc文件中有如下类:
class ExternalObject : public nsIXPCScriptable {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD GetClassName(char **class_name) {
NS_ENSURE_ARG_POINTER(class_name);
*class_name = nsCRT::strdup(EXTOBJ_CLASSNAME);
return NS_OK;
}
NS_IMETHOD GetScriptableFlags(PRUint32 *scriptable_flags) {
NS_ENSURE_ARG_POINTER(scriptable_flags);
*scriptable_flags = WANT_GETPROPERTY | WANT_SETPROPERTY;
return NS_OK;
}
NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper,
JSContext *cx, JSObject *obj, jsval id,
jsval *vp, PRBool *ret_val) {
std::string json;
NS_ENSURE_TRUE(JSONEncode(cx, id, &json), NS_ERROR_FAILURE);
std::string result = SendFeedback(kGetPropertyFeedback, cx,
json.c_str(), NULL);
if (result == “\”\\\”function\\\”\”") {
JSFunction *function = JS_NewFunction(cx, InvokeFunction, 0, 0,
obj, json.c_str());
NS_ENSURE_TRUE(function, NS_ERROR_FAILURE);
JSObject *func_obj = JS_GetFunctionObject(function);
NS_ENSURE_TRUE(func_obj, NS_ERROR_FAILURE);
*vp = OBJECT_TO_JSVAL(func_obj);
} else if (result == “\”\\\”undefined\\\”\”") {
*vp = JSVAL_VOID;
} else {
NS_ENSURE_TRUE(JSONDecode(cx, result.c_str(), vp), NS_ERROR_FAILURE);
}
*ret_val = PR_TRUE;
return NS_OK;
}
NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper,
JSContext *cx, JSObject *obj, jsval id,
jsval *vp, PRBool *ret_val) {
std::string name_json, value_json;
NS_ENSURE_TRUE(JSONEncode(cx, id, &name_json), NS_ERROR_FAILURE);
NS_ENSURE_TRUE(JSONEncode(cx, *vp, &value_json), NS_ERROR_FAILURE);
SendFeedback(kSetPropertyFeedback, cx,
name_json.c_str(), value_json.c_str(), NULL);
*ret_val = PR_TRUE;
return NS_OK;
}
……略
};
在第一段程序中的ScriptableInterface *ext_obj = details_view_data ->GetExternalObject(); 得到的就是上面这个类,而这个类中的方法又恰恰是JS到C++的映射,从这个文件的代码中还发现了其它一些会调用libgtkembedmoz库的函数,如gtk_moz_embed_new(),gtk_moz_embed_load_url()等等,而libgtkembedmoz(qt中为libqt4-webkit)是一个精简的浏览器引擎,由此可判断Google gadget engine中关于HTML/CSS内容的显示都是通过libgtkembedmoz(qt中为libqt4-webkit,Windows中也有对应的库)库来实现的。这种方法虽然存在一定冗余(包含的libxml2也支持HTML解析),但实现起来由于两个库分别进行XML和HTML的解析,结构比较清晰,而且采用专门的浏览器引擎库解析HTML/CSS速度更快,容错性更高。
结 论
Google gadget engine可以在pluginHelper这个插件窗口中(采用pluginHelper.showDetailsView函数)显示HTML/CSS内容,实现方法是通过解析固定的resource://html_details_view.xml文件绘制窗口,其中的JavaScript函数间接调用(映射成C++)精简浏览器库libgtkembedmoz(qt中为libqt4-webkit)进行HTML/CSS的解析并显示。