您的位置:首页 > 其它

chromium浏览器页面longclick弹出菜单功能的实现

2015-04-02 10:02 603 查看
最开始做这个功能是在chromium34上面实现的,后来移植到39上面,调用的相关的系统和内核的底层的接口还都好用,从34到39版本变化,chromium内核对于事件的传递这块逻辑代码应该没有太大的变化。

首先说下webkit浏览器是如何实现长按网页弹出菜单的:

从最开始的说起,对于android使用原生webview的浏览器来讲,长按一个链接(当然也包括图片,网站,邮箱,手机号码等),都会弹出一个菜单供用户选择,当然用户点击长按的内容不同,菜单弹出来的内容也不一样。那么系统把判断用户点击的类别大致包括下面几类:WebView.HitTestResult.PHONE_TYPE,WebView.HitTestResult.EMAIL_TYPE,WebView.HitTestResult.GEO_TYPE,WebView.HitTestResult.IMAGE_TYPE,WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE,WebView.HitTestResult.SRC_ANCHOR_TYPE,WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE。

这些在一个android原生浏览器的代码中,都是可以看到的,上面也写出了类名,方便大家查找。

那么这些信息,在应用层是如何得到的呢?在webview依赖的activity中,有一个方法:

@Override

public void onCreateContextMenu(ContextMenu menu, View v,

ContextMenuInfo menuInfo) {

}

这其中第二个参数,返回的webview就能够获取到WebView.HitTestResult的相关信息(当然这块不是所有的时候都会返回webview,我在这里说的我想大家应该都明白,我就不像写代码那样严谨的描述了)。接下来就是处理的相关逻辑了,这里不再赘述,自己看代码就好了。

接下来说下chromiun内核的浏览器,应用层可以copy安卓原生浏览器的代码,非常简单,和底层的交互就略有些复杂。在实现这个功能的过程中,主要做两部分内容的工作:一部分是响应事件;另一部分是是将点击事件的位置信息传到底层,底层处理后将获取到的信息再反给上层。

先在这里插一句:我们是将contentshell封装成自己的webview,提供了和android原生的相似的接口供上层调用,这也符合软件开发的基本原则。

1.事件响应:想要得到onCreateContextMenu回调,需要webview performLongclick,contentView中的performLongclick也要设置为返回true,这样才能保证一层一层的传给activity

2.将点击事件的位置信息传到底层,底层处理后将获取到的信息再反给上层:

webview的onTouchEvent传给Shell进行处理,在shell中调用nativeRequestNewTestDataAt方法,对应的c++文件是shell_android.cc文件,添加RequestNewTestDataAt方法,再调用shell_render_view_host_ext.cc中的RequestNewTestDataAt方法,这个方法有两个参数(Int view_x, int view_y),这两个就是坐标信息。前面的操作都是在主进程中进行,我们要把这两个参数通过browser进程发送给render进程。我们在shell_render_view_message.h中注册IPC通信的代码:

(browser to render)

IPC_MESSAGE_ROUTED2(ShellViewMsg_DoHitTest, int, int)

(render to browser)

IPC_MESSAGE_ROUTED1(ShellViewHostMsg_UpdateHitTestData, content::ShellHitTestData)

这块注册了两个IPC_MESSAGE信息,分别用于收发。

发送消息流程:shell_render_view_host_ext.cc中的RequestNewTestDataAt进行发送;

shell_render_view_ext.cc中进行接收,下面是主要代码:

(省略了大部分详细处理过程,省略的这些在chromium39源码中都能找到)

void ShellRenderViewExt::OnDoHitTest(int view_x, int view_y) {

if (!render_view() || !render_view()->GetWebView())

return;

const blink::WebHitTestResult result =

render_view()->GetWebView()->hitTestResultAt(

blink::WebPoint(view_x, view_y));

ShellHitTestData data;

if (!result.urlElement().isNull()) {

data.anchor_text = result.urlElement().innerText();

data.href = GetHref(result.urlElement());

}

PopulateHitTestData(result.absoluteLinkURL(),

result.absoluteImageURL(),

result.isContentEditable(),

&data);

Send(new ShellViewHostMsg_UpdateHitTestData(routing_id(), data));

}

将处理后的信息,封装成ShellHitTestData再次发送给browser进程,注意,shell_render_view_ext.cc是在子进程中进行的。

render进程再次将处理后封装好的东西发送给browser进程。(ShellHitTestData是在shell_hit_test_data.cc中创建的,有的代码里面叫AwHitTestData,这里不再贴代码了)

下面是在shell_render_view_host_ext.cc中处理的代码(主进程)

void ShellRenderViewHostExt::OnUpdateHitTestData(

const ShellHitTestData& hit_test_data) {

DCHECK(CalledOnValidThread());

last_hit_test_data_ = hit_test_data;

has_new_hit_test_data_ = true;

}

这里面同时也要提供接口:

const ShellHitTestData& ShellRenderViewHostExt::GetLastHitTestData() const {

DCHECK(CalledOnValidThread());

return last_hit_test_data_;

}

我们在shell_android.cc中调用上面的接口:

void Shell::UpdateLastHitTestData(JNIEnv* env, jobject obj) {

if (!shell_render_view_host_ext_->HasNewHitTestData()) return;

const ShellHitTestData& data = shell_render_view_host_ext_->GetLastHitTestData();

shell_render_view_host_ext_->MarkHitTestDataRead();

// Make sure to null the Java object if data is empty/invalid.

ScopedJavaLocalRef<jstring> extra_data_for_type;

if (data.extra_data_for_type.length())

extra_data_for_type = ConvertUTF8ToJavaString(

env, data.extra_data_for_type);

ScopedJavaLocalRef<jstring> href;

if (data.href.length())

href = ConvertUTF16ToJavaString(env, data.href);

ScopedJavaLocalRef<jstring> anchor_text;

if (data.anchor_text.length())

anchor_text = ConvertUTF16ToJavaString(env, data.anchor_text);

ScopedJavaLocalRef<jstring> img_src;

if (data.img_src.is_valid())

img_src = ConvertUTF8ToJavaString(env, data.img_src.spec());

Java_Shell_updateHitTestData(env,

obj,

data.type,

extra_data_for_type.obj(),

href.obj(),

anchor_text.obj(),

img_src.obj());

}

上面代码中最后一个掉用的方法,刚好是回调Shell.java中的方法,相当于转了一圈又回到了应用层,调用java层的updateHitTestData方法中包含了一些参数,其中data.type就是最前面说的哪几种类型,后面就不说了,源码中bean里面的注释都有自己看吧。

接下来就是在onCreateContextMenu中进行处理了,回调也有了,数据也传回来了,接下来全都是在java层处理的代码,打开新窗口,下载等操作了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐