修改浏览器使之支持WML格式页面

对于移动终端,有时候服务器返回的是WML格式的页面。 比如说中国移动的一些需要使用cmwap接入点的业务页面(DCD, 移动梦网…), 这就要求终端浏览器必须能够支持对WML格式页面的解析和显示。 Android原始代码里的webkit层虽然提供了WML相关的解析类,但是并没有很好地支持,所以在页面上无法正确显示。 我们需要做以下一些修改:
1. 打开对WML格式解析的通道
修改源码的\external\webkit\WebCore\dom\DOMImplementation.cpp
获取到服务器返回的数据中的content-type字段值后,会调用这个类里面的isXMLMIMEType()方法来判断是否按照XML格式来解析。我们看这个方法:
?
1
2
3
4
5
6
7
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类型加进去
?
1
If(mimeType == “text/vnd.wap.wml”) return true ;

修改framework/base/core/java/android/webkit/LoadListener.java, 源码如下:
?
1
2
3
4
5
6
7
8
9
// 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中处理点击事件的方法:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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();
     }
}

通过打印Log发现,getAttribute(HTMLNames::hrefAttr)获取的只是href后面的字符串,包含变量$(). 我们需要对其中的变量进行转化。还好WMLVariables里面已经提供了相应的方法substituteVariableReferences,不需要我们再去写一个了。修改如下
#include "WMLVariables.h"
?
1
2
3
4
5
6
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中提供接口, 返回链接。
?
1
2
3
4
5
6
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的方法是这样的:
?
1
2
3
4
WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node) {
     WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
     return anchor ? anchor->href() : WebCore::String();
}

在这里增加WML的支持, 修改如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**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();
}

还有获取链接标题的方法,修改如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
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:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 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" ;
             }
         }
     }
}


你可能感兴趣的:(浏览器,XHTML,String,url,webkit,Parsing)