PPAPI插件与浏览器的通信
PPAPI的插件,原本是可以使用JS与浏览器交互的,https://code.google.com/p/ppapi/wiki/InterfacingWithJavaScript,这里还提供了一个JS与plugin交互的文档,但现在说不支持了,现在应该通过PPB_Messaging接口来完成Plugin和浏览器的交互,具体参考https://src.chromium.org/viewvc/chrome/trunk/src/ppapi/c/ppb_messaging.h?revision=92312&view=markup这里。我实验了一下,通了。
Messaging接口很好,传递的消息可以自已定义,类型也无限制,非常方便。
foruok原创,如需转载请关注foruok的微信订阅号“程序视界”联系foruok。
使用postMessage通信
Messaging接口与其它大多数接口一样,分PPB和PPP两侧。分开来说明一下要做的事情。
插件侧
要做这么些事情:
- 实现PPP_Messaging接口,关键是void (*HandleMessage)(PP_Instance instance, struct PP_Var message)方法
- 在Get_Interface中返回名字是PPP_MESSAGING_INTERFACE的接口
- 在PPP_InitializeModule中获取 PPB_Messaging、PPB_Var、PPB_VarArray、PPB_VarDictionary等接口。
PPB_Messaging的PostMessage用于向浏览器发送消息,发送过去的消息,JS代码可以接收到。
PPB_Var可以用来构造String类型的Var,可以操作Var的引用计数
PPB_VarArray是数组接口,可以创建、访问、设置数组
PPB_VarDictionary是字典(map)接口,可以创建字典Var,可以存取key-value对。 - PPP_Messaging的HandleMessage处理浏览器的消息,如果需要,调用PPB_Messaging的PostMessage发送消息.
注意,插件侧调用PPB_Var接口的VarFromUtf8时,传入的len不包括’\0’在内。PPAPI的ppb_var.h的注释里的示例代码片段有误,调用时传递的长度是sizeof(hello_world),应该减去一。
还有一点,插件和浏览器交互的数据,都是数据的拷贝哦,调用接口会发生复制行为。
浏览器侧
浏览器侧可以使用JavaScript来监听插件发送的message事件,也可以使用插件元素的postMessage发送消息给插件。
基本上做下面几件事即可:
- 实现处理消息的JS函数,其参数是MessageEvent,data成员为插件发过来的信息,可以当做JS对象来访问。
- 监听插件的message事件
- 在合适的时候调用插件的postMessage(object)方法发送消息给插件
代码
分插件代码和HTML代码。
插件代码
代码是在在PPAPI插件中创建本地窗口一文示例代码的基础上改的,添加了消息处理的部分。只贴相关的部分了。
获取消息相关接口的代码
在PPP_InitializeModule中添加了获取PPB_Messaging接口以及其它可能用到的PP_Var类型的接口,有Array和Dictionary。
g_var_interface = (const PPB_Var*)get_browser_interface(PPB_VAR_INTERFACE); g_dictionary_interface = (const PPB_VarDictionary*)get_browser_interface(PPB_VAR_DICTIONARY_INTERFACE); g_array_interface = (const PPB_VarArray*)get_browser_interface(PPB_VAR_ARRAY_INTERFACE); g_message_interface = (const PPB_Messaging*)get_browser_interface(PPB_MESSAGING_INTERFACE);
- 1
- 2
- 3
- 4
PPP_Messaging接口的实现
PPP_Messaging接口的实现代码如下:
void Plugin_HandleMessage(PP_Instance instance, struct PP_Var message){ char szLog[256] = { 0 }; sprintf_s(szLog, 256, "Plugin_HandleMessage, type = %d\r\n", message.type); OutputDebugStringA(szLog); if (message.type == PP_VARTYPE_DICTIONARY) { char command[] = "command"; struct PP_Var commandKey = g_var_interface->VarFromUtf8(command, sizeof(command) - 1); struct PP_Var commandVar = g_dictionary_interface->Get(message, commandKey); int len = 0; const char *strCommand = g_var_interface->VarToUtf8(commandVar, &len); g_var_interface->Release(commandKey); sprintf_s(szLog, 256, "Plugin_HandleMessage, dict, command = %s, len = %d\r\n", strCommand, len); OutputDebugStringA(szLog); if (len == 0) { OutputDebugString(_T("Tang_plugin, recv invalid command\r\n")); g_var_interface->Release(commandVar); return; } if (strncmp(strCommand, "joinConf", len) == 0) { char confIdKey[] = "confId"; char userNameKey[] = "userName"; char *szConfId = 0; char*szUserName = 0; struct PP_Var idKey = g_var_interface->VarFromUtf8(confIdKey, sizeof(confIdKey) - 1); struct PP_Var userKey = g_var_interface->VarFromUtf8(userNameKey, sizeof(userNameKey) - 1); struct PP_Var var = g_dictionary_interface->Get(message, idKey); const char *value = g_var_interface->VarToUtf8(var, &len); if (len > 0) { szConfId = malloc(len + 1); strncpy_s(szConfId, len+1, value, len); szConfId[len] = 0; } g_var_interface->Release(var); var = g_dictionary_interface->Get(message, userKey); value = g_var_interface->VarToUtf8(var, &len); if (len > 0) { szUserName = malloc(len + 1); strncpy_s(szUserName, len+1, value, len); szUserName[len] = 0; } g_var_interface->Release(var); sprintf_s(szLog, 256, "Plugin_HandleMessage, dict, command = joinConf, user = %s, confId = %s\r\n", szUserName, szConfId); OutputDebugStringA(szLog); if (szConfId && szUserName) { sprintf_s(szLog, 256, "plugin got confId - %s, user - %s\r\n", szConfId, szUserName); OutputDebugStringA(szLog); joinConf(szConfId, szUserName); } else { OutputDebugString(_T("Invalid conference id or userName\r\n")); } if (szConfId) free(szConfId); if (szUserName) free(szUserName); g_var_interface->Release(idKey); g_var_interface->Release(userKey); /* fake attendees*/ char szMsgTypeValue[] = "userlist"; char szTypeKey[] = "type"; struct PP_Var typeKey = g_var_interface->VarFromUtf8(szTypeKey, sizeof(szTypeKey) - 1); struct PP_Var typeValue = g_var_interface->VarFromUtf8(szMsgTypeValue, sizeof(szMsgTypeValue) - 1); struct PP_Var attendee = g_dictionary_interface->Create(); g_dictionary_interface->Set(attendee, typeKey, typeValue); struct PP_Var userArray = g_array_interface->Create(); char szUser1[] = "ZhangSan"; char szUser2[] = "LiSi"; struct PP_Var user1 = g_var_interface->VarFromUtf8(szUser1, sizeof(szUser1) - 1); struct PP_Var user2 = g_var_interface->VarFromUtf8(szUser2, sizeof(szUser2) - 1); g_array_interface->Set(userArray, 0, user1); g_array_interface->Set(userArray, 1, user2); char szValueKey[] = "value"; struct PP_Var valueKey = g_var_interface->VarFromUtf8(szValueKey, sizeof(szValueKey) - 1); g_dictionary_interface->Set(attendee, valueKey, userArray); g_message_interface->PostMessage(instance, attendee); OutputDebugString(_T("Post attendee to browser")); g_var_interface->Release(typeKey); g_var_interface->Release(typeValue); g_var_interface->Release(user1); g_var_interface->Release(user2); g_var_interface->Release(valueKey); g_var_interface->Release(userArray); g_var_interface->Release(attendee); } else if (strncmp(strCommand, "viewVideo", len) == 0) { char userIdKey[] = "userId"; char *szUserId = 0; struct PP_Var idKey = g_var_interface->VarFromUtf8(userIdKey, sizeof(userIdKey) - 1); struct PP_Var var = g_dictionary_interface->Get(message, idKey); const char *value = g_var_interface->VarToUtf8(var, &len); if (len > 0) { szUserId = malloc(len + 1); strncpy_s(szUserId, len + 1, value, len); szUserId[len] = 0; } if (szUserId) { sprintf_s(szLog, 256, "plugin got userId - %s\r\n", szUserId); OutputDebugStringA(szLog); viewVideo(szUserId, g_child_window); } else { OutputDebugString(_T("Invalid viewVideo command without userId\r\n")); } if (szUserId) free(szUserId); g_var_interface->Release(var); g_var_interface->Release(idKey); } g_var_interface->Release(commandVar); } else if (message.type == PP_VARTYPE_STRING) { char hello_world[] = "Hello world!"; struct PP_Var var = g_var_interface->VarFromUtf8(hello_world, sizeof(hello_world) - 1); g_message_interface->PostMessage(instance, var); // var will be copyed g_var_interface->Release(var); }}static PPP_Messaging message_interface = { &Plugin_HandleMessage};
- 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
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
有点长,比较潦草。
返回PPP_Messaging的代码
完整的PPP_GetInterface函数如下:
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) { if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) { OutputDebugString(_T("PPP_GetInterface, instance_interface\r\n")); return &instance_interface; } else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0) { OutputDebugString(_T("PPP_GetInterface, input_interface\r\n")); return &input_interface; } else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) { OutputDebugString(_T("PPP_GetInterface, message_interface\r\n")); return &message_interface; } return NULL;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
网页代码
网页代码如下:
<!DOCTYPE html><html> <!-- Copyright (c) 2016 foruok@微信订阅号“程序视界”(programmer_sight). All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --><head> <style type="text/css">#contacts { margin:10px; width:300px; height:200px; background-color:gray; }</style> <script type="text/javascript">function handleMessage(message) { alert(message.data.type); if(message.data.type.localeCompare("userlist") == 0){ var i = 0; ul = document.getElementById("attendee"); for(; i < message.data.value.length; i++){ var li = document.createElement("li"); li.appendChild(document.createTextNode(message.data.value[i])); ul.appendChild(li); } } } function joinConference(){ plugin = document.getElementById('tangplugin'); plugin.postMessage({ command:"joinConf", confId: document.getElementById("confId").value, userName: document.getElementById("userName").value }); } function viewSharedVideo(){ plugin = document.getElementById('tangplugin'); plugin.postMessage({ command:"viewVideo", userId: document.getElementById("userId").value }); } function initialize() { plugin = document.getElementById('tangplugin'); plugin.addEventListener('message', handleMessage, false); } document.addEventListener('DOMContentLoaded', initialize, false);</script> <title>Tang in Plugin</title></head><body><form>ConferenceID: <input type="text" id="confId" /> User: <input type="text" id="userName" /> <input type="button" value="Join" onclick="joinConference()"/></form><hr><div id="contacts"><p>contacts:</p><ul id="attendee"> <!-- here will show attendee list, added by JS callback --></ul>UserId:<input type="text" id="userId" /> <button type="button" onclick="viewSharedVideo()">View Video</button></div><p>share video:</p><embed id="tangplugin" type="application/x-ppapi-tang-video" width="300px" height="200px" top="40px" left="20px"></body></html>
- 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
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
Join按钮会获取它前面两个文本框的内容,发送给插件,插件返回一个用户列表,网页解析(HandleMessage方法)出来,动态修改用户列表。
运行效果
贴两幅图,点击Join按钮之前是酱紫的:
点击Join按钮后是酱紫的:
其他参考文章:
- CEF Windows开发环境搭建
- CEF加载PPAPI插件
- VS2013编译最简单的PPAPI插件
- 理解PPAPI的设计
- PPAPI插件与浏览器的交互过程
- Windows下从源码编译CEF
- 编译PPAPI的media_stream_video示例
- PPAPI插件的绘图与输入事件处理
- 在PPAPI插件中创建本地窗口
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow
PPAPI插件与浏览器的通信相关推荐
- PPAPI插件与浏览器的交互过程
上一篇理解了一下PPAPI的设计,并从代码角度理解了一下相关主题,这篇文章关注下面几点: 插件实例对象的创建与使用流程 实例大小的确认 渲染(绘图) 处理输入事件 foruok原创,如需转载请关注fo ...
- PPAPI插件的全屏切换处理
有时你会想让PPAPI插件全屏(比如播放视频时),这次来看看怎么做. PPAPI和CEF App两侧都要处理. foruok原创,转载请注明出处.欢迎关注foruok的订阅号"程序视界&qu ...
- PPAPI插件的绘图与输入事件处理
在PPAPI插件与浏览器的交互过程一文中学习了PPAPI插件与浏览器的交互流程.渲染逻辑.输入事件的处理逻辑,这次我们改造一下graphics_2d_example示例,加入处理鼠标事件的逻辑,演示一 ...
- 新版谷歌Chrome取消对PPAPI插件支持后,浏览器网页打开编辑保存微软Office、金山WPS文档解决方案
最近陆续看到一些大学发布公告,谷歌Chrome取消了对PPAPI插件支持,导致某些在线Office厂家产品将无法在谷歌Chrome107及以上版本运行,被迫更换360浏览器或者使用低版本Chrome浏 ...
- PPAPI插件与Node 插件对比
最近公司项目需要从cef向electron过度,相应的插件也需要做升级,其实ppapi插件在electron中也是可以工作的,只是某些场景效率会差一些. 以下是个人看法,有错误之处请不吝指正. PPA ...
- Python+selenium 自动化-启用带插件的chrome浏览器,调用浏览器带插件,浏览器加载配置信息。
正常的话我们启用的 chrome 浏览器是不带插件的,如果你能登陆 chrome 的话,你会发现登陆信息也没有,还有不管你怎样设置每次新打开的 chrome 都是默认设置的. 我们正常启动的浏览器每次 ...
- VBS操作IE ---(【当不使用IE时】可以使用Chrome插件,自定义JS插件操作浏览器)
目录 ■前言 ■举个简单VBS操作IE的例子 --- ■如何让IE打开是IE,而不是Edge ■其他更多VBS相关 ・更多操作一览 ・按键 shift ctrl alt ・打开图片,以幻灯片形式显示 ...
- 2020年最好用的chrome插件-CSDN浏览器助手评测
收到CSDN活动链接:2020年最好用的chrome插件评测活动 迫不及待下载测试,效果怎么样呢?大家慢慢跟我一起来体验. 1.下载chrome插件-CSDN浏览器助手 地址:https://plug ...
- via浏览器如何使用插件 Via浏览器添加使用插件教程
1.尽管Via浏览器的体积小巧,但其中提供的功能可谓是五脏俱全,而想要体验更多服务的话,就需要去安装插件了,这需要大家点击右上角的横杠选项,并且从列表里选择"设置"功能. via浏 ...
- FoxyProxy插件在浏览器的配置
FoxyProxy插件在浏览器的配置 FoxyProxy是一个Firefox扩展,它能自动切换一个或多个基于URL模式的代理服务器的网络连接.简单来说,FoxyProxy自动化了Firefox的连接设 ...
最新文章
- HDU4858 项目管理 其他
- 常见的服务器内存浅析
- Flink SQL 的 9 个示例
- 通过rxjs的一个例子, 来学习SwitchMap的使用方法
- P1368-工艺【最小表示法】
- onenote快捷键_高效飞快地使用onenote快捷键:快捷键功能架构解析
- 原来国家的名字可以如此的浪漫~~!
- 测试面试题集-1.测试基础理论
- Httpclient gzip 乱码问题解决
- C++ enum类型的一个更好的用法
- mysql handbook_MySQL 8 Administrator’s Guide
- c 语言游戏代码大全,C语言经典游戏代码
- 机器视觉软件工程师的生活是怎样的?
- 计算机启动dos,开机怎么进入dos_开机怎么进入dos界面
- 【技术邻】FloEFD热仿真分析之结果处理
- 本地上传文件到服务器
- pattern.compile java_Java Pattern compile(String)用法及代码示例
- ABR算法研究综述 | A Survey on Bitrate Adaptation Schemes for Streaming Media Over HTTP(IEEE COMST‘18)阅读笔记
- ArcGIS Engine10.0轻松入门级教程(1)——必备基础知识
- 大明战神戚继光带给程序员的启示
热门文章
- Primer Premier 6.0 for Win 专业的引物设计
- 对三款软件的测评、分析和建议
- 最全事业单位考试计算机基础知识试题,最全的事业单位考试计算机基础知识试题...
- 高级语言程序设计(c语言版)课后答案,高级语言程序设计习题与解答(C语言版)/高等院校教材...
- (超详细)Eclipse使用教程——使用Eclipse创建第一个HelloWorld!
- Windows7WithSP1/TeamFoundationServer2012update4/SQLServer2012
- hotnets 2019 Using ground relays for low-latency wide-area routing in megaconstellations 阅读报告
- 今日头条张一鸣:做CEO要避免理性的自负
- 通过 DataEase 获取 API 数据完成项目周报分享
- java web学习步骤,javaweb学习路线有哪些?如何学习?