跟进一个 jserror 问题带来的思考

作者:徐杰

wns-cgi的业务使用同学反馈了一个问题:在安卓手Q的空间里,打开小游戏,然后进入空间宠物,聊天窗口,发送消息。这里会注册一个jsbridge回调,发送消息后,回调后,小游戏页面会报一个js的error,比较奇怪,报错截图如下

报错信息倒是很明确,是因为方法未定义导致的异常;触发条件是因为宠物聊天用的是wns-cgi,所以最初怀疑是否和wns-cgi有关,就由我和xixinhuang一起在跟进这个问题;庆幸的是问题必现,这样我们就可以复现和构造各种场景;下面简单总结下定位的过程:A打开了B页面,B页面用了wns-cgi进行聊天(打开了输入法控制面板),B页面没出现异常,但是A页面的jserror报错了;

(1)根据报错信息最初是想确认为什么A页面会收到这个回调?明明是在B页面的操作啊?于是赶紧拉上了客户端同学和mqq.js的开发同学进行定位,可是没有什么头绪和进展,客户端同学觉得没问题啊,调用wns-cgi,客户端正常的执行了一段回调;wns-cgi的请求对象如下:

{"method":"post","url":"http://h5.qzone.qq.com/webapp/json/pet_chat/Chat?uin=2577199125&g_tk=830846970&t=0.36583463430175467","body":{"qua":"V1_AND_SQ_7.2.5_0_RDM_B","iEventID":2,"stCommInfo.lLoginUin":2577199125,"stCommInfo.lHostUin":2577199125,"stCommInfo.stGeoInfo.lLon":113933731,"stCommInfo.stGeoInfo.lLat":22540520,"stCommInfo.stGeoInfo.lAlt":0,"stCommInfo.strQua":"V1_AND_SQ_7.2.5_0_RDM_B","strText":"好红红火火恍恍惚惚","mapExtInfo.source":"h5","mapExtInfo.gpstype":"1","mapExtInfo.devicetype":"1","mapExtInfo.device_info":"","mapExtInfo.userPetId":10116,"mapExtInfo.hostPetId":10116,"mapExtInfo.first_sentence_id":"","strCookie":""},"timeout":0,"dataNeedBase64":false,"callback":"__MQQ_CALLBACK_AUTO_35"} 客户端

客户端处理后,loadurl调用的js代码如下:

(window.mqq && mqq.version > 20140616001 && mqq.execGlobalCallback||function(cb) {window[cb] && window[cb].apply(window, [].slice.call(arguments, 1));}).apply(window, ["__MQQ_CALLBACK_AUTO_35", {"status":4,"url":"http:\/\/h5.qzone.qq.com\/webapp\/json\/pet_chat\/Chat?uin=2577199125&g_tk=830846970&t=0.36583463430175467","code":0,"dataIsBase64":false,"data":"{\"ret\":0,\"msg\":\"\",\"data\":{\"stActionSet\":{\"iID\":0,\"vecActionInfo\":[{\"vecActionNode\":[{\"vecName\":[\"f_speak\"],\"iLoopCount\":1,\"fSpeed\":1}],\"stContentInfo\":{\"iContentType\":1,\"strText\":\"小宠的语文阔是数学老师教的哦~ 诗词歌赋样样精通!你想考我吗!\",\"strUrl\":\"\",\"vecChoice\":[],\"iStayTimeMs\":-1,\"stFile\":{\"iFileId\":0,\"strName\":\"\",\"strUrl\":\"\",\"strMd5\":\"\",\"iSize\":0,\"iFileType\":0,\"iWidth\":0,\"iHeight\":0},\"stUgcVideo\":{\"strVideoId\":\"\",\"iAppId\":0,\"iVideoWidth\":0,\"iVideoHeight\":0,\"iOperateMask\":0,\"iVideoState\":0,\"uiDurationTimeMs\":0,\"strVideoUrl\":\"\",\"strCoverImgUrl\":\"\",\"iCoverWidth\":0,\"iCoverHeight\":0,\"strOrgKey\":\"\",\"strCurKey\":\"\",\"strCid\":\"\",\"strUgcKey\":\"\",\"lUin\":0,\"strNick\":\"\"},\"vecPetQuickCommentPic\":[],\"stRelationChain\":{\"iTotal\":0,\"vecFriInfo\":[]},\"stFmInfo\":{\"strShowId\":\"\",\"strShowName\":\"\",\"strAudioUrl\":\"\",\"strCoverUrl\":\"\",\"strOwner\":\"\",\"iShowDuration\":0,\"strShowUrl\":\"\"}},\"strTrace\":\"\",\"iDelayMs\":0}]},\"mapExtInfo\":{},\"stValidator\":{\"lUin\":2577199125,\"strModelKey\":\"mao_phase1_20172217\"},\"strCookie\":\"chat_session_id=0A9E20724B9A4AF28A0CECA0BE2FC8CF\"}}","httpStatusCode":200,"header":{"x-powered-by":"TSW\/Node.js","cache-control":"no-cache","connection":"keep-alive","keep-alive":"timeout=10","vary":"Origin, Accept","mod-map":"webapp_json","content-type":"application\/json; charset=UTF-8","content-length":"1082","date":"Wed, 13 Sep 2017 07"}}]);

看起来调用wns-cgi没啥问题啊,问题重新回到了我们这边;

(2)我和xixin同学开始了错误的重新定位过程,既然报错是来自A页面,一定是和mqq的什么事件监听有关,之前我处理过mqq.debug.js文件,先定位到和这个错误有关的代码,是在invokeClientMethod中注册的,如下图:

那我们代理上这个mqq.js,然后在这个方法中加上console.trace()看看调用栈,会不会有帮助?

同时我们在A页面有涉及到mqq.invoke等方法的地方,能加上callback的,我们统一都加上测试代码,比如类似的代码,如果A页面有执行这个callback,我们可以从对应的ns和method入手查:

var img = new Image();
img.src = 'https://h5.qzone.qq.com/proxy/domain/www.qq.com/xxx/xxx';

结果上面的img的请求根本没发出来,而且我们在invokeClientMethod中设置的错误信息,只有openUrl的日志,这个是合理的,因为A打开B页面,A调用的就是openUrl,我们感觉有些没辙了。。。客户端没问题,H5也没问题,问题在哪?明明有jserror报错,而且也很怀疑是和事件监听有关,可是我们H5能做的比较有限;

(3)峰回路转:

在和xixin重现错误的时候,碰到一个常见的联调问题,就是js缓存,经常需要重新清理缓存,而且也可能代理不到本地,给重现带来一定的困扰;比如A打开B,如果A加上了_proxy=1走到了wns-html,可能都没有js请求,这样我们设置的什么代理都没用,清理缓存也不一定行。。。很焦急的看着时间慢慢流逝。。。没办法,只能先把url地址修改下,去掉_proxy=1,在聊天消息框中打开对应的url,总算可以代理上了,可以看到正常的invokeClientMethod的日志信息,意外出现了,在AIO聊天窗口打开的页面,居然没有报错???为什么?目前来看已有的信息,就是为什么从好友动态进入,一定会报错,而从QQ聊天点击链接进去就不会报错?都是同样打开的webview,H5的代码没变,那我们唯一可以确定的就是webview有什么差别;

只能继续求助于客户端,客户端保罗大神pauloliu根据这个线索,对比了代码,结果如下:

空间和AIO里面打开url,用的都是一个activity,但是确实aio里面打开的不会有问题。
AIO里面打开url的intent:
Bundle[{qqBrowserActivityCreateTime=1505354889492, fling_action_key=2, from_aio=1, param_force_internal_browser=false, friendUin=373922647, from_aio_opt=1, preAct=QQBrowserDelegationActivity, leftViewText=返回, aio_open_web=true, fling_code_key=199961721, fromAio=true, from_aio_time=43533567, uinType=0, articalChannelId=2, useDefBackText=true, startOpenPageTime=1505354885699, uin=2577199125, url=https://h5.qzone.qq.com/h5plus/home/index/alpha?_proxy=1&_wv=3&_nav_alpha=0&from=qqnavigation, preAct_time=1505354885699, key_isReadModeEnabled=true, needSkey=true, injectrecommend=true, curtype=0}]
空间的
Bundle[{qqBrowserActivityCreateTime=1505355030872, key_isFromQZone=true, fling_action_key=2, preAct=QQBrowserDelegationActivity, leftViewText=返回, fling_code_key=158123890, articalChannelId=5, startOpenPageTime=1505355027800, source_name=QQ空间, uin=2577199125, url=https://h5.qzone.qq.com/h5plus/home/index/alpha?_proxy=1&_wv=3&_nav_alpha=0&from=qqnavigation, fromQZone=true, preAct_time=1505355027800, key_isReadModeEnabled=true, needSkey=true, injectrecommend=true, insertPluginsArray=[Ljava.lang.String;@27f8d6b, isNeedAdvReport=false, post_data=null}]

我们都发现了下面多了一个insertPluginsArray,这个是干嘛的?insertPluginsArray代码如下:

在浏览器中插入了一个Qzone的命名空间,这个是啥?Qzone是个大插件,里面东西太多了,保罗发现干掉这个插件就OK了,那证明问题就出在这个Qzone插件上;找到源头我们也感觉有了一丝丝希望;在客户端同学努力下,问题根源找到了:

输入面板关闭的时候,会发出一个广播,而这个广播的接收者在webview初始化的时候就被创建了,所以每个web页面都可以收到。。。所以问题的原因我们来回归下:

我们从小游戏页面进入宠物页面,在宠物页面聊天的时候,会调用TopicComment这个方法,同时也会注册一个回调;关闭输入框会执行一下这个callback,方法名是上面的报错的哪个匿名方法,这个回调方法命名规则就是MQQ_CALLBACKAUTO+SN(这个SN的值UUIDSeed自增生成),在A页面因为没有注册这个回调方法,但是在A页面却收到了这个广播,这个是浏览器层面的事件监听,但是没有对应的回调方法,所以报错了,不过不影响功能,只是影响了jserror的统计;客户端同学也已经修复了这个bug,在下个版本会兼容处理;

通过这次的问题定位,对我们未来碰到类似的问题进行解决的时候,提供了思路,就是客户端可能会在浏览器加载的时候,不时的插入一些包括js,事件等等,如果没有相应的代码进行辅助证明,可以从这个角度去定位,举一反三下 :)

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏全栈工程师成长之路

全栈开发自学路线

这里筑梦师,是一名正在努力学习的iOS开发工程师,目前致力于全栈方向的学习,希望可以和大家一起交流技术,共同进步,用简书记录下自己的学习历程.

47213
来自专栏快乐八哥

Windows8异步编程的注意事项

Windows8项目中涉及到下载数据和上传数据。针对小的数据使用的是WinJS.xhr(),而针对大的文件,不方便使用post方式直接上传的文件都使用Backg...

16510
来自专栏IMWeb前端团队

Vuex 模块化实现待办事项的状态管理

本文作者:IMWeb 林鑫 原文出处:IMWeb社区 未经同意,禁止转载 前言 在vue里,组件之间的作用域是独立的,父组件跟子组件之间的通讯可以通过...

1919
来自专栏快乐八哥

Windows8异步编程的注意事项

Windows8 App开发中涉及到下载数据和上传数据。针对小的数据使用的是WinJS.xhr(),而针对大的文件,不方便使用post方式直接上传的文件都使用B...

19310
来自专栏腾讯IVWEB团队的专栏

Web 推送技术

伴随着今年 Google I/O 大会的召开,一个很火的概念--Progressive Web Apps 诞生了。这代表着我们 web 端有了和原生 APP 媲...

1.4K0
来自专栏青玉伏案

设计模式(八): 从“小弟”中来类比"外观模式"(Facade Pattern)

在此先容我拿“小弟”这个词来扯一下淡。什么是小弟呢,所谓小弟就是可以帮你做一些琐碎的事情,在此我们就拿“小弟”来类比“外观模式”。在上面一篇博文我们完整的介绍了...

19410
来自专栏FreeBuf

KindEditor开源富文本编辑框架XSS漏洞

0×01 前言 KindEditor 是一套开源的在线HTML编辑器,主要用于让用户在网站上获得所见即所得编辑效果,开发人员可以用 KindEditor 把传统...

2518
来自专栏java闲聊

WebMagic初探,了解爬虫

1473
来自专栏夏时

利用微博当图床-php语言实现

4024
来自专栏林冠宏的技术文章

独立开发 一个社交 APP 的架构分享 (已实现)

(本博客为原创:https://cloud.tencent.com/developer/user/1148436/activities) My BananaCl...

4118

扫码关注云+社区