Android瀏覽器Browser二次開發(二) 修改瀏覽器使之支持WML格式頁面

 

Android瀏覽器Browser二次開發(二)

 

第二章 修改瀏覽器使之支持WML格式頁面。
對 於移動終端,有時候服務器返回的是WML格式的頁面。 比如說中國移動的一些需要使用cmwap接入點的業務頁面(DCD, 移動夢網…), 這就要求終端瀏覽器必須能夠支持對WML格式頁面的解析和顯示。 Android原始代碼裏的webkit層雖然提供了WML相關的解析類,但是並沒有很好地支持,所以在頁面上無法正確顯示。 我們需要做以下一些修改:
1. 打開對WML格式解析的通道
修改源碼的/external/webkit/WebCore/dom/DOMImplementation.cpp
獲取到服務器返回的數據中的content-type字段值後,會調用這個類裏面的isXMLMIMEType()方法來判斷是否按照XML格式來解析。我們看這個方法:
bool DOMImplementation::isXMLMIMEType(const String& mimeType)
{
if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "text/xsl")
return true;
static const char* const validChars = "[0-9a-zA-Z_//-+~!$//^{}|.%'`#&*]"; // per RFCs: 3023, 2045
DEFINE_STATIC_LOCAL(RegularExpression, xmlTypeRegExp, (String("^") + validChars + "+/" + validChars + "+//+xml$", TextCaseSensitive));
return xmlTypeRegExp.match(mimeType) > -1;
}
這裏只包含了text/xml, application/xml, text/xls. 我們需要把WML相應的MiMeType類型加進去
If(mimeType == “text/vnd.wap.wml”) return true;

修改framework/base/core/java/android/webkit/LoadListener.java, 源碼如下:
// Does the header parsing work on the WebCore thread.
private void handleHeaders(Headers headers) {

} else if (mMimeType.equals("text/vnd.wap.wml")) {
// As we don't support wml, render it as plain text
mMimeType = "text/plain";

}
我們可以看到, 原來是不支持wml格式的, 都當做text/plain來處理了,這樣顯然是不能正確顯示的。 所以這一行mMimeType = "text/plain";需要註釋掉,打開給外圍。
2. 對WML中的超鏈接元素(WMLAElement和WMLAnchorElement)中href屬性值裏面的變量替換。
筆 者發現,在一個WML的登陸頁面上,填入用戶名和密碼後,點擊登陸,附加到url後面的用戶名和密碼是$(username) 和$(password) ,有web開發經驗的XDJM都知道,這是沒有將變量替換爲頁面上相應值。我們看WMLAElement.cpp中處理點擊事件的方法:
void WMLAElement::defaultEventHandler(Event* event)
{
if (isLink() && (event->type() == eventNames().clickEvent || (event->type() == eventNames().keydownEvent && focused()))) {
MouseEvent* e = 0;
if (event->type() == eventNames().clickEvent && event->isMouseEvent())
e = static_cast<MouseEvent*>(event);

KeyboardEvent* k = 0;
if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent())
k = static_cast<KeyboardEvent*>(event);

if (e && e->button() == RightButton) {
WMLElement::defaultEventHandler(event);
return;
}

if (k) {
if (k->keyIdentifier() != "Enter") {
WMLElement::defaultEventHandler(event);
return;
}

event->setDefaultHandled();
dispatchSimulatedClick(event);
return;
}

if (!event->defaultPrevented() && document()->frame()) {
String url = document()->completeURL(deprecatedParseURL(getAttribute(HTMLNames::hrefAttr)));
/
document()->frame()->loader()->urlSelected(url, target(), event, false, false, true, SendReferrer);
}

event->setDefaultHandled();
}

WMLElement::defaultEventHandler(event);
通 過打印Log發現,getAttribute(HTMLNames::hrefAttr)獲取的只是href後面的字符串,包含變量$(). 我們需要對其中的變量進行轉化。還好WMLVariables裏面已經提供了相應的方法substituteVariableReferences,不需 要我們再去寫一個了。修改如下
#include "WMLVariables.h"
。。。
if (!event->defaultPrevented() && document()->frame()) {
// Substitute variables within target url attribute value. String href = getAttribute(HTMLNames::hrefAttr);
href = substituteVariableReferences(href, document(), WMLVariableEscapingEscape);
String url = document()->completeURL(deprecatedParseURL(href));
document()->frame()->loader()->urlSelected(url, target(), event, false, false, true, SendReferrer);
}

別忘了,WMLAnchorElement.cpp中相應的地方也要同樣改掉。
3. 在頁面上長按鏈接時彈出選項點擊失效
這是由於點擊時是從webkit層去獲取這個鏈接的地址和標題的, 而源碼中只考慮了HTML格式的頁面, WML頁面被忽略了。 返回的href爲null.
首先要在WMLAElement.cpp中提供接口, 返回鏈接。
KURL WMLAElement::href() const
{
// Substitute variables within target url attribute value.
String href = substituteVariableReferences(getAttribute(HTMLNames::hrefAttr),
document(), WMLVariableEscapingEscape);
return document()->completeURL(href);
}
由於WMLAnchorElement繼承了WMLAElement, 就不需要再添加這個方法了。
然後修改WebViewCore.cpp, 原來獲取href的方法是這樣的:
WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
{
WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
return anchor ? anchor->href() : WebCore::String();
}
在這裏增加WML的支持, 修改如下:
/**add WML anchor support. 20110224, begin**/
#if ENABLE(WML)
WebCore::WMLAnchorElement* WebViewCore::retrieveWMLAElement(WebCore::Frame* frame, WebCore::Node* node)
{
if (!CacheBuilder::validNode(m_mainFrame, frame, node))
return 0;
if (!node->hasTagName(WebCore::WMLNames::aTag))
return 0;
return static_cast<WebCore::WMLAnchorElement*>(node);
}
#endif
/**add WML anchor support 20110224, end**/

WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
{
/**retrieve WMLAnchor element. 20110224, begin**/
#if ENABLE(WML)
if (node->isWMLElement()) {
WebCore::WMLAnchorElement* anchor = retrieveWMLAElement(frame, node);
return anchor ? anchor->href() : WebCore::String();
}
#endif
/**retrieve WMLAnchor element. 20110224, end**/
WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
return anchor ? anchor->href() : WebCore::String();
}
還有獲取鏈接標題的方法,修改如下:
WebCore::String WebViewCore::retrieveAnchorText(WebCore::Frame* frame, WebCore::Node* node)
{
/**retrieve WMLAnchor element. 20110224, begin**/
#if ENABLE(WML)
if (node->isWMLElement()) {
WebCore::WMLAnchorElement* anchor = retrieveWMLAElement(frame, node);
return anchor ? anchor->title() : WebCore::String();
}
#endif
/**retrieve WMLAnchor element. 20110224, end**/
WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
return anchor ? anchor->text() : WebCore::String();
}
4. 移動夢網無法正確顯示,解析出錯。
移 動夢網返回的數據格式爲application/vnd.wap.xhtml+xml, 包含了xhtml和xml兩種格式。而CMCC的數據本身又不是嚴格按照W3C標準來的, 導致在解析的時候出現了語法錯誤提示。 對於這種情況,我們顯然無法去要求CMCC改變數據, 只能把這種格式當做普通的html來顯示, html沒有那麼嚴格的語法檢查, 可以正常顯示。修改framework/base/core/java/android/webkit/LoadListener.java:
// Does the header parsing work on the WebCore thread.
private void handleHeaders(Headers headers) {
。。。
String contentType = headers.getContentType();
if (contentType != null) {
parseContentTypeHeader(contentType);

// If we have one of "generic" MIME types, try to deduce
// the right MIME type from the file extension (if any):
if (mMimeType.equals("text/plain") ||
mMimeType.equals("application/octet-stream")) {

// for attachment, use the filename in the Content-Disposition
// to guess the mimetype
String contentDisposition = headers.getContentDisposition();
String url = null;
if (contentDisposition != null) {
url = URLUtil.parseContentDisposition(contentDisposition);
}
if (url == null) {
url = mUrl;
}
String newMimeType = guessMimeTypeFromExtension(url);
if (newMimeType != null) {
mMimeType = newMimeType;
}
} else if (mMimeType.equals("text/vnd.wap.wml")) {
// As we don't support wml, render it as plain text
// mMimeType = "text/plain";
} else {
// It seems that xhtml+xml and vnd.wap.xhtml+xml mime
// subtypes are used interchangeably. So treat them the same.


//if (mMimeType.equals("application/vnd.wap.xhtml+xml")) {
// mMimeType = "application/xhtml+xml";
//}
/* Webkit used libxml2 as the xml parser, but the CMCC's WAP sites
written in WML or XHTML do not meet W3C's specification well,
and libxml2 will throw a lot of grammatical errors when it parses
the document which has the mime type is "application/xhtml+xml" or
"application/vnd.wap.xhtml+xml". When i opened the macro named
"XHTMLMP" in config.h in webcore and tested, i found some bugs,
i believed that Google did not do detail test works for this macro.
So i could handle "XHTML" mime type as "HTML" only in order to
open CMCC's WAP sites in browser.
*/

if (mMimeType.equals("application/vnd.wap.xhtml+xml") ||
mMimeType.equals("application/xhtml+xml"))
{
mMimeType = "text/html";

}

 

 

該篇文章轉自:http://seya.javaeye.com/blog/931289

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章