4

我正在尝试在 Delphi 中构建一个 nsIProtocolHandler 实现。(我之前成功地完成了一个 IInternetProtocol,并希望在 FireFox 中拥有我在 Internet Explorer 中所拥有的东西。)感谢d-gecko项目,它以某种方式将 TInterfacedObject 魔法与 nsISupports 魔法联系起来,我能够制作一个在被询问时提供 nsIModule 的 DLL,在被询问时提供 nsIFactory,在被询问时提供我的 nsIProtocolHandler 之一,在被询问时提供我的 nsIChannel/nsIHttpChannel 之一。

当使用 firefox.exe 作为主机进程进行调试时,我可以看到我的库被加载,NewURI 被调用了 3 次,NewChannel 被调用,并且我传递了一个实现 nsIChannel 和nsIHttpChannel的对象。

这就是我困扰的地方。在我从 AsyncOpen 返回控制权之前,我不应该在我得到的 nsIStreamListener 上调用 OnStartRequest 和 OnDataAvailable,但我似乎没有在调用 AsyncOpen 的线程中重新获得控制权。

我尝试使用围绕默认http处理程序的自制包装器进行调试(使用CreateInstanceByContractID('@mozilla.org/network/protocol;1?name=http',...)。我也把监听器包裹过去了。奇怪的是,我看到 OnStartRequest 和 OnDataAvailable 在我的通道包装器死后在同一个线程中被调用。但谁在打电话?如果它是我试图包装的 http 通道,它如何生存(在同一个线程中)以及它如何控制调用侦听器?我很困惑。并且卡住了。

我曾尝试联系 d-gecko 项目的主要开发人员,但没有得到回应。

(另外,有人注意到我在 nsIProtocolHandler 上 MDC 讨论页底部的简介吗?)

(哦,还有一件事,是的,我知道“生活会更简单”,如果我只是从 C++ 中的 nsBaseChannel 继承。但重点是在现有的 Delphi 项目核心中添加一个 FireFox 协议处理程序。)

更新:我已经阅读了更多内容,这里也提到了:“流侦听器的方法在调用 asyncOpen [...] 的线程上被调用”,但是如果不先从“托管应用程序”调用,这怎么可能? ,我不清楚。这是XPCOM的把戏吗?我想在我得到它之前我必须阅读(很多)更多的 Firefox 源代码。

4

1 回答 1

1

我对 Mozilla 编码一无所知,但它就在这里。

根据nsIChannel::asyncOpen()

异步打开该通道。数据在可用时被馈送到指定的流侦听器。流侦听器的方法在调用 asyncOpen 的线程上被调用,直到 asyncOpen 返回后才被调用。如果 asyncOpen 成功返回,则通道承诺至少调用 onStartRequest 和 onStopRequest。

因此,作为协议处理程序,您自己实现一个通道对象或将其重定向到一个通道对象,通道的使用者使用asyncOpen(). 由于它是异步调用,因此想法是立即将控制权返回给使用者,并且假设在加载数据时调用回调。

我不确定我是否理解您所说的“但我似乎无法在调用 AsyncOpen 的线程中重新获得控制权”。线程由协议的使用者创建,并打开通道。

同样来自nsIChannel::asyncOpen()

如果 asyncOpen 成功返回,则通道负责保持自身处于活动状态,直到它在 aListener 上调用 onStopRequest 或调用 onChannelRedirect。

由于 asyncOpen 会立即返回控制权,因此通道本身需要在某个地方保持活动状态。

如果您正在寻找示例代码,我发现 codease 非常有用。请参阅nsIProtocolHandlernsIChannel。使用它我遇到了视图源协议(这个实现可能更旧,但没关系)。

nsViewSourceHandler实现自定义通道。

nsViewSourceHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
{
    nsresult rv;

    nsViewSourceChannel* channel;
    rv = nsViewSourceChannel::Create(nsnull, NS_GET_IID(nsIChannel), (void**)&channel);
    if (NS_FAILED(rv)) return rv;

    rv = channel->Init(uri);
    if (NS_FAILED(rv)) {
        NS_RELEASE(channel);
        return rv;
    }

    *result = NS_STATIC_CAST(nsIViewSourceChannel*, channel);
    return NS_OK;
}

这是nsViewSourceChannelAsyncOpen:

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;
}

无论如何,这是一个漫长而曲折的询问方式,您是如何创建频道的?

于 2009-05-27T08:02:55.350 回答