漫步于第三方线上代码中——记一次排查内嵌谷歌地图在 ios 上黑屏问题

2890次阅读  |  发布于2年以前

某日,qa 反馈说『ios 上的谷歌地图显示不出来,黑屏啦~』,我一边心想本周已经是第 x 个内嵌链接的 bug 了,这些第三方链接还能不能好了一边很不追求极致的嘟囔着为什么第三方链接的 bug 也要我看,一边苦逼的打开 ipad 模拟器,投入到痛苦的 safari debug 工作中。

safari, 新时代的 ie

问题表现如下:

排查思路

一开始的我没有很清晰的解决思路,想着会不会是谷歌地图对 iPad 兼容有问题,因此看了下 iPad 的 safari 浏览器是否也有问题,发现并没有。

然后我想会不会是 ios 嵌入文档时,对谷歌地图的一些资源进行了拦截,导致其直接黑屏了,因此我对比了一下 network 访问,如下:

❌ 不正常的情况:

⭕️ 正常的情况:

可以看到正常的时候确实比不正常的时候访问多了很多资源,但是其中并没有什么访问失败的部分,所以拦截的猜想也是错的。

后来,我对比了下两边的 html 结构,发现正常的时候,mapDiv 这个元素下会有内容,而不正常的时候是空的,如下:

可见,正常情况下谷歌地图会往这个 div 里插入地图的内容,这个问题的根源在于,谷歌地图不知道出于什么原因没有往 mapDiv 里插入内容。

到这里为止应该是很容易想到的部分,那么,我们怎么才能知道是哪个地方导致的呢?

debug 谷歌地图

找到 appendChild 的地方

要往一个 div 里插入内容,最容易想到的方法是什么?答案显然易见,就是 appendChild(或者 append),所以我在控制台里改造了一下 Element 的原型,使每一次 appendChild 时,我都能看到日志,如下:

const appendChild = Element.prototype.appendChild;
// 这个地方能否使用箭头函数呢?^-^
Element.prototype.appendChild = function(...args) {
  console.log('append', this.className);
  if (this.id === 'mapDiv') {
    debugger;
  }
  appendChild.bind(this, ...args)();
}

改造完后,可以发现文档中的 appendChild 都会打出日志了,如下:

但是,此时重新访问谷歌地图 iframe,却看不到任何 append 的日志,这是为什么呢?我突然想起,iframe 处于一个沙箱中,我这直接在控制台里输的行为只能改页面的 Element 的原型,却没法改到 iframe 里的 Element。那此时该怎么办呢?

通过 override 插入改造代码

在控制台里打应该是不行了,所以我们的眼光只能放到谷歌地图的脚本上,只要把我们的改造代码加到 js 中,那 iframe 一跑就会有我们的代码了。因此我借助 chrome 本来就提供的 override 功能对谷歌入口 js 进行了改造。为了可能没用过 override 的同学,这边详细说下这个功能如何使用,已经会的同学可以跳过这个部分

how to use override?

首先我们在 Sources 标签页下找到 override,并开启 Enable Local Overrides。

如果你是第一次使用,此时 chrome 应该会让你提供一个空文件夹,用于存放 override 的文件,随便找个路径新建个文件夹叫 override,然后选择这个 override 的文件夹即可。

如果 chrome 报错说此文件夹无法读写,需要给这个路径开通读写权限,mac 下输入命令 sudo chmod 777 ${路径} 即可(没记错的话,作者 mac 命令用的不是很好~~~)

然后找到入口文件,然后右键选择 Save for overrides (一定要是原件,formatted 后的版本不行)

然后就可以看到 overrides 文件夹里有这个 js 文件啦~此时可以随意更改并保存,刷新后即可应用更新!

因为 chrome 自带的 format 对这个 js 效果很差,还是有一大坨代码堆积,难以 debug。所以我私下用 vscode 的 prettier 格式化了一下

开始 debug!

通过加入改造代码,我们在访问谷歌地图的时候轻松找到了 js 栈,如下:

后来经过漫长的 F9、F10、上下前后选栈,以及大量的脑筋急转弯后,找到了问题原因:

这个地方,会在初始化的时候拿 body 的宽度,如果宽高都是 0, 会返回一个 false,导致 append 的逻辑进不去。由于 doc1.0 是通过 document.createElement 创建 iframe,然后再插到文档中的,而且一开始并没有宽高,因此可能引发谷歌地图这个问题,造成黑屏。

解决方案

在 iframe load 后再刷新下谷歌地图。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8