7

我遇到了一个非常棘手的技术问题,我希望周围可能有一些 Webkit 专家。我正在为客户开发 iOS 应用程序。大多数应用程序是在 UIWebView 控制器中提供的 HTML5 内容。

大约一周前,QA 团队开始报告应用程序崩溃。上周我们每天收到大约 1 份崩溃报告。不幸的是,它们是那种无法确定一致地重现崩溃的明确步骤的崩溃。奇怪的是,其中一些崩溃报告一直在使用旧版本的 iOS 代码库——这些代码已经成功运行了几个月,而没有人注意到这种崩溃行为。

但所有崩溃情况的共同点是,它们都是针对提供最新版本 HTML Web 应用程序页面的更新后端运行的。所以看起来我们在服务器端做了一些新的事情,触发了 iOS 代码中的某些东西崩溃。

崩溃日志非常一致。这是符号化的日志:

0   WebCore    0x33147ab0 WebCore::FrameLoader::cancelledError(WebCore::ResourceRequest const&) const + 4
1   WebCore    0x33070fbe WebCore::ResourceLoader::init(WebCore::ResourceRequest const&) + 166
2   WebCore    0x33070e66 WebCore::SubresourceLoader::startLoading() + 14
3   WebCore    0x33070c4e WebCore::ResourceLoadScheduler::servePendingRequests(WebCore::ResourceLoadScheduler::HostInformation*, WebCore::ResourceLoadPriority) + 46
4   WebCore    0x33076508 WebCore::ResourceLoadScheduler::servePendingRequests(WebCore::ResourceLoadPriority) + 36
5   WebCore    0x32fd38c8 WebCore::ThreadTimers::sharedTimerFiredInternal() + 92

(大多数讨论 WebCore 中的崩溃的问题都建议您在 dealloc 方法中将 webview.delegate 设置为 nil。这似乎不是我们的问题)。

现在我有一个理论(我稍后会谈到),但我没有明确的证据。因此,我从 webkit.org 获取了源代码,并试图阅读足够多的内容以了解 WebKit 在崩溃时正在做什么。我认为我使用的 WebKit 源版本与 iOS 设备中的版本不同(我们在 5.0.1 和 5.1.1 设备中看到了这一点),关键方法似乎引用了下载资源(比如CSS 和图像),但似乎涉及到一个空 URL,所以我们最终调用了 cancelledError 方法。

然后 FrameLoader 执行此操作:

ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
{
    ResourceError error = m_client->cancelledError(request);
    error.setIsCancellation(true);
    return error;
}

正是在这种方法中,应用程序崩溃了:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000008

这向我表明 m_client 没有指向有效的东西。

现在,我确实有一个关于正在发生的事情的理论,只是基于直觉和间接证据。

我们的 UIWebView 有一个委托,它评估正在加载到 Web 视图中的 URL。在某些情况下,我们决定在单独的 ViewController 中启动新的 URL,如下所示:

- (BOOL)webView:(UIWebView *)source shouldStartLoadWithRequest:(NSURLRequest *)request  navigationType:(UIWebViewNavigationType)navigationType 
{
    ...

    if ([self.popupStrategy shouldPopupURL:[request URL] fromCurrent:[source.request URL]]) {

        PopupTransitionViewController *popController = [self createPopupController:request];
        ... push it onto the navigation controller ...

    }
    ...
}

导致此弹出策略返回 true 的关键条件之一发生在跨域链接期间。也就是说,如果用户点击链接/图标并且该链接的目标由第三方站点托管,则应用程序会在不同的 ViewController 中启动新内容(出于各种原因,包括能够获得跨域链接上的良好原生转换)。

几周前发生的服务器端更改之一是链接 href 已更新——该链接现在调用我们的主服务器,该服务器发回 HTTP 重定向,将客户端发送到第三方站点。

我在这种情况下看到的是我们的 popupStrategy 被调用了两次。第一次,它评估我们主服务器的 URL,第二次,它评估第三方 URL。在第二种情况下,该策略告诉 UIWebView 在新的 ViewController 中加载请求。我的想法是,Webkit 代码中的某些内容并不总是这样,并且通过一些奇怪的时间或诸如此类的东西,这在某种程度上导致了崩溃。

我之所以坚持这个理论,是因为它基于以前在我们的服务器代码库中不存在的新 Web 加载行为,这很容易符合症状。我对 Webkit 代码的阅读是,在引用的一些方法中,Webkit 在看到跨域请求时似乎正在做一些特殊的处理。但是崩溃不可能在提示时重现,所以我们没有更多的事情要做。但如果这个理论是正确的,我知道一个合理的解决办法。

我希望有人对 Webkit 的内部有一定的了解并可以建议:

a) Webkit 堆栈跟踪对这一理论的支持程度如何?

b)我得到的堆栈跟踪是否有任何其他人可以看到的见解?

4

1 回答 1

0

我最终根据我上面描述的理论进行了代码更改。进行这些更改后,我没有看到崩溃再次发生。所以原来的理论看起来是正确的。

于 2012-08-08T13:22:38.110 回答