现在的位置: 首页 > 综合 > 正文

修改Android Webkit使浏览器支持WML格式的页面

2013年08月13日 ⁄ 综合 ⁄ 共 8413字 ⁄ 字号 评论关闭

    网上很多贴子关于修改android源码来支持WML,但都说得不全面。最近一个项目需要WML功能,自己亲手做了一遍,费了很多周折,终于调通了。特把修改共享给大家。

    我是在androidv2.3.5上修改的,其他版本估计大同小异。android源码上自带有WML解析功能,但默认把ENABLE_WML关闭了。所以要添加WML, 总共要修改如下文件:

external/webkit/Android.mk

external/webkit/WebCore/Android.mk

external/webkit/WebCore/Android.derived.mk

external/webkit/JavaScriptCore/wtf/Platform.h

external/webkit/webkit/android/jni/WebViewCore.h

external/webkit/webkit/android/jni/WebViewCore.cpp

external/webkit/WebCore/wml/WMLAElement.h

external/webkit/WebCore/wml/WMLAElement.cpp

external/webkit/WebCore/dom/DOMImplementation.cpp

frameworks/base/core/java/android/webkit/LoadListener.java

 下面详细说明如何修改,红色部分表示需要添加/修改的代码。

(一) 修改编译文件

1. external/webkit/JavaScriptCore/wtf/Platform.h中添加:

#if PLATFORM(ANDROID)
。。。。
#define ENABLE_WML       1 

#endif

这段非必需, 最有效的办法是直接在命令行打开特定feature, 这样定义的feature在.mk和所有源文件中都可见。如:

mm ENABLE_WML=true ENBLE_SVG=true

 

2. external/webkit/Android.mk中添加:

ifeq ($(ENABLE_WML), true)

 LOCAL_C_INCLUDES := $(LOCAL_C_INCLUDES) \
       $(LOCAL_PATH)/WebCore/wml

endif

查找LOCAL_CFLAGS += -DENABLE_SVG=1,模仿加一段:

ifeq ($(ENABLE_WML),true)
LOCAL_CFLAGS += -DENABLE_WML=1
endif

(这段非必需,不加也能编译通过)

 

3.external/webkit/WebCore/Android.derived.mk中添加:

查找style_sheets := $(style_sheets) $(LOCAL_PATH)/css/svg.css,类似加上如下脚本,

这种脚本会在\out\target\product\generic\obj\STATIC_LIBRARIES\libnfwebcore_intermediates

\WebCore\css下生成包含wmlUserAgentStyleSheet[]的UserAgentStyleSheets.h/UserAgentStyleSheets.cpp:

ifeq ($(ENABLE_WML), true) 

style_sheets := $(style_sheets) $(LOCAL_PATH)/css/wml.css
endif

 然后再文件最后加上如下脚本,该脚本会在\out\target\product\generic_x86\obj\STATIC_LIBRARIES\libwebcore_intermediates\WebCore下生成指定的文件:

#WML attribute names

ifeq ($(ENABLE_WML), true)
GEN:= $(intermediates)/WMLNames.cpp $(intermediates)/WMLNames.h $(intermediates)/WMLElementFactory.cpp $(intermediates)/WMLElementFactory.h
$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
$(GEN): PRIVATE_CUSTOM_TOOL = perl -I $(PRIVATE_PATH)/bindings/scripts $< --tags $(wml_tag) --attrs $(wml_attrs) --factory --wrapperFactory --output $(dir $@)
$(GEN): wml_tag := $(LOCAL_PATH)/wml/WMLTagNames.in
$(GEN): wml_attrs := $(LOCAL_PATH)/wml/WMLAttributeNames.in
$(GEN): $(LOCAL_PATH)/dom/make_names.pl $(wml_tag) $(wml_attrs)
        $(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
endif

 

4. external/webkit/WebCore/Android.mk中添加如下代码,让WebCore/wml下的文件参加编译:

ifeq ($(ENABLE_WML), true)
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
 wml/WMLAccessElement.cpp \
 wml/WMLAElement.cpp \
 wml/WMLAnchorElement.cpp \
 wml/WMLBRElement.cpp \
 wml/WMLCardElement.cpp \
 wml/WMLDocument.cpp \
 wml/WMLDoElement.cpp \
 wml/WMLElement.cpp \
 wml/WMLErrorHandling.cpp \
 wml/WMLEventHandlingElement.cpp \
 wml/WMLFieldSetElement.cpp \
 wml/WMLFormControlElement.cpp \
 wml/WMLGoElement.cpp \
 wml/WMLImageElement.cpp \
 wml/WMLImageLoader.cpp \
 wml/WMLInputElement.cpp \
 wml/WMLInsertedLegendElement.cpp \
 wml/WMLIntrinsicEvent.cpp \
 wml/WMLIntrinsicEventHandler.cpp \
 wml/WMLMetaElement.cpp \
 wml/WMLNoopElement.cpp \
 wml/WMLOnEventElement.cpp \
 wml/WMLOptGroupElement.cpp \
 wml/WMLOptionElement.cpp \
 wml/WMLPageState.cpp \
 wml/WMLPElement.cpp \
 wml/WMLPostfieldElement.cpp \
 wml/WMLPrevElement.cpp \
 wml/WMLRefreshElement.cpp \
 wml/WMLSelectElement.cpp \
 wml/WMLSetvarElement.cpp \
 wml/WMLTableElement.cpp \
 wml/WMLTaskElement.cpp \
 wml/WMLTemplateElement.cpp \
 wml/WMLTimerElement.cpp \
 wml/WMLVariables.cpp
endif

   至此,WML功能编译部分添加完毕,此时生成的libwebcore.so已经包含wml功能。

 

(二) 修改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中处理点击事件的方法:

添加包含:

#include "WMLVariables.h"
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 0  


        if (!event->defaultPrevented() && document()->frame()) {
            String url = document()->completeURL(deprecatedParseURL(getAttribute(HTMLNames::hrefAttr)));
            document()->frame()->loader()->urlSelected(url, target(), event, false, false, true, SendReferrer);
        }
#else 
        if (!event->defaultPrevented() && document()->frame()) {
            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);
        }
#endif
        event->setDefaultHandled();
    }

    WMLElement::defaultEventHandler(event);
}

3. 在页面上长按链接时弹出选项点击失效
这是由于点击时是从webkit层去获取这个链接的地址和标题的, 而源码中只考虑了HTML格式的页面, WML页面被忽略了。返回的href为null。

首先要在WMLAElement.h中声明一个函数, 返回链接。
添加包含:

#include "KURL.h"

添加声明:

virtual KURL href() const;


然后在WMLAElement.cpp中实现该函数, 返回链接。
KURL WMLAElement::href() const
{
String href = substituteVariableReferences(getAttribute(HTMLNames::hrefAttr),
document(), WMLVariableEscapingEscape);
return document()->completeURL(href);
}

再修改WebViewCore.h, 添加如下代码:

添加包含:

#if ENABLE(WML)

#include "WMLNames.h"

#include "WMLAElement.h"

#include "WMLAnchorElement.h"

#endif

namespace WebCore {

    ....
    class HTMLSelectElement;
#if ENABLE(WML)    

    class WMLAnchorElement;
#endif

    class RenderPart;

    ...
}

class WebViewCore : public WebCoreRefObject {

        .....
        bool handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr);
        WebCore::HTMLAnchorElement* retrieveAnchorElement(WebCore::Frame* frame, WebCore::Node* node);
#if ENABLE(WML) 
        WebCore::WMLAnchorElement* retrieveWMLAElement(WebCore::Frame* frame, WebCore::Node* node);
#endif

        ...

};

最后修改WebViewCore.cpp, 添加/修改如下代码:
#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


WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
{
#if ENABLE(WML) 
    if (node->isWMLElement()) {
        WebCore::WMLAnchorElement* anchor = retrieveWMLAElement(frame, node);
        return anchor ? anchor->href() : WebCore::String();
    }
#endif

    WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
    return anchor ? anchor->href() : WebCore::String();
}

WebCore::String WebViewCore::retrieveAnchorText(WebCore::Frame* frame, WebCore::Node* node)
{
#if ENABLE(WML) 

    if (node->isWMLElement()) {
       WebCore::WMLAnchorElement* anchor = retrieveWMLAElement(frame, node);
       return anchor ? anchor->title() : WebCore::String();
    }
#endif
    WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(frame, node);
    return anchor ? anchor->text() : WebCore::String();
}

 

至此,WML功能全部添加完成!

WML测试网页: http://121.8.100.132:8080/fb/index.jsp

原文链接: http://blog.sina.com.cn/s/blog_49f62c3501012dpo.html

抱歉!评论已关闭.