2

我正在 Delphi 中构建一个 nsIProtocolHandler 实现。(更多在这里)而且它已经在工作了。模块构建的数据通过 nsIInputStream 流式传输。我已经让所有 nsIRequest、nsIChannel 和 nsIHttpChannel 方法和属性都在工作。

我已经开始测试,但遇到了一些奇怪的事情。我有一个带有这个简单 HTML 的页面“a.html”:

<img src="a.png">

"xxm://test/a.html" 和 "xxm://test/a.png" 都可以在 Firefox 中使用,并提供上面的 HTML 或 PNG 图像数据。问题在于显示 HTML 页面时,图像未加载。当我调试时,我看到:

  • 为 a.png 调用 NewChannel,(当 Firefox 在 a.html 上处理 OnDataAvailable 通知时),
  • NotificationCallbacks 已设置(我只需要保留一个引用,对吗?)
  • RequestHeader " Accept" 设置为 " image/png,image/*;q=0.8,*/*;q=0.5"
  • 但是随后,通道对象被释放(很可能是由于引用计数为零)

查看其他请求,我希望设置其他一些属性(例如 LoadFlags 或 OriginalURI)并调用AsyncOpen,从那里我可以开始响应请求。

有人认识这个吗?难道我做错了什么?也许使用 LoadFlags 或 LoadGroup?我不确定何时在 LoadGroup 上调用 AddRequest 和 RemoveRequest,并从nsHttpChannelnsBaseChannel 窥视我不确定是早点还是晚点(在 OnStartRequest 或 OnStopRequest 之前或之后)调用 RemoveRequest 更好?

更新:检查了全新的 Firefox 3.5,还是一样

更新:为了进一步隔离问题,我尝试使用“file://test/a1.html”,<img src="xxm://test/a.png" />但仍然只能得到以上发生的事件序列。如果我应该将此辅助请求添加到负载组以调用 AsyncOpen,我不知道从哪里获得对它的引用。

还有更多:我发现只有一个“Accept”字符串实例被添加到请求标头中,它在创建新通道后立即查询 nsIHttpChannelInternal,但我什至没有通过这个 QueryInterface 调用......(我发布了它在这里

4

2 回答 2

0

又是我。

我将引用相同的内容nsIChannel::asyncOpen()

如果asyncOpen成功返回,则通道负责保持自己处于活动状态,直到它调用 onStopRequestaListener调用 onChannelRedirect

如果你回到nsViewSourceChannel.cpp,有一个地方loadGroup->AddRequest被调用,两个地方loadGroup->RemoveRequest被调用。

nsViewSourceChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
{
    NS_ENSURE_TRUE(mChannel, NS_ERROR_FAILURE);

    mListener = aListener;

    /*
     * We want to add ourselves to the loadgroup before opening
     * mChannel, since we want to make sure we're in the loadgroup
     * when mChannel finishes and fires OnStopRequest()
     */

    nsCOMPtr<nsILoadGroup> loadGroup;
    mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    if (loadGroup)
        loadGroup->AddRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                             this), nsnull);

    nsresult rv = mChannel->AsyncOpen(this, ctxt);

    if (NS_FAILED(rv) && loadGroup)
        loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                this),
                                 nsnull, rv);

    if (NS_SUCCEEDED(rv)) {
        mOpened = PR_TRUE;
    }

    return rv;
}

nsViewSourceChannel::OnStopRequest(nsIRequest *aRequest, nsISupports* aContext,
                               nsresult aStatus)
{
    NS_ENSURE_TRUE(mListener, NS_ERROR_FAILURE);
    if (mChannel)
    {
        nsCOMPtr<nsILoadGroup> loadGroup;
        mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
        if (loadGroup)
        {
            loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                    this),
                                     nsnull, aStatus);
        }
    }
    return mListener->OnStopRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                   this),
                                    aContext, aStatus);
}

编辑

由于我不知道 Mozilla 是如何工作的,所以我必须通过阅读一些代码来猜测。从频道的角度来看,一旦加载了原始文件,它的工作就完成了。如果要加载像图像一样链接在文件中的辅助项目,则必须在侦听器中实现它。请参阅TestPageLoad.cpp。它实现了一个粗略的解析器,并在以下条件下检索子项OnDataAvailable

NS_IMETHODIMP
MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
                            nsIInputStream *stream,
                            PRUint32 offset, PRUint32 count)
{
    //printf(">>> OnDataAvailable [count=%u]\n", count);
    nsresult rv = NS_ERROR_FAILURE;
    PRUint32 bytesRead=0;
    char buf[1024];

    if(ctxt == nsnull) {
      bytesRead=0;
      rv = stream->ReadSegments(streamParse, &offset, count, &bytesRead);
    } else {
      while (count) {
        PRUint32 amount = PR_MIN(count, sizeof(buf));
        rv = stream->Read(buf, amount, &bytesRead);  
        count -= bytesRead;
      }
    }

    if (NS_FAILED(rv)) {
      printf(">>> stream->Read failed with rv=%x\n", rv);
      return rv;
    }

    return NS_OK;
}

重要的是它调用streamParse(),它查看和元素的src属性,调用,它使用新的侦听器和调用创建新通道。imgscriptauxLoad()AsyncOpen()

uriList->AppendElement(uri);
rv = NS_NewChannel(getter_AddRefs(chan), uri, nsnull, nsnull, callbacks);
RETURN_IF_FAILED(rv, "NS_NewChannel");

gKeepRunning++;
rv = chan->AsyncOpen(listener, myBool);
RETURN_IF_FAILED(rv, "AsyncOpen");

由于它MyListener在那里传递了另一个对象实例,因此也可以无限加载更多子项目,就像俄罗斯娃娃的情况一样。

于 2009-07-02T08:17:31.083 回答
0

我想我找到了(我自己),仔细看看这个页面。为什么它没有突出显示 UUID 已更改版本,我不清楚,但它可以解释为什么在 nsIHttpChannelInternal 上调用 QueryInterface 时(或之前)会失败。

使用新的(呃)UUID,我得到了更好的结果。正如我在问题更新中提到的,我已经在 bugzilla.mozilla.org 上发布了这个,我很好奇我是否会得到响应以及会得到哪个响应。

于 2009-07-03T11:57:06.673 回答