用Delphi实现nsIProtocolHandler

时间:2009-05-14 16:43:32

标签: delphi firefox delphi-7 gecko

我正在尝试在Delphi中构建一个nsIProtocolHandler实现。 (我之前已经成功完成了一个IInternetProtocol,并希望在FireFox中拥有我在Internet Explorer中所拥有的功能。) 感谢the d-gecko项目,它以某种方式将TInterfacedObject魔法链接到nsISupports魔法,我能够创建一个在被询问时提供nsIModule的DLL,它在被询问时提供nsIFactory,当我被问到时,它提供了我的一个nsIProtocolHandler's ,当被问到时,它提供了我的一个nsIChannel / nsIHttpChannel。

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

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

我尝试使用自制的包装器来调试默认的http处理程序(得到CreateInstanceByContractID('@mozilla.org/network/protocol;1?name=http', ...)。我也包裹了听众传递。奇怪的是,我看到OnStartRequest和OnDataAvailable在我的通道包装器死后,在同一个线程中被称为。但谁在打电话?如果它是我试图包装的http频道,它是如何存活的(在同一个线程中)以及如何获得控制来调用侦听器?我很困惑。卡住了。

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

(另外,有人注意到我在talk page on MDC on nsIProtocolHandler底部的模糊处理吗?)

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

更新:我已经做了一些阅读,还提到了here:“流式监听器的方法是  在调用asyncOpen [...]的线程上调用了“但是如果不首先从'托管应用程序'调用它是怎么可能的,我不清楚。这是一个XPCOM技巧吗?我想我将不得不阅读(很多)在得到它之前有更多的firefox源码。

1 个答案:

答案 0 :(得分:1)

我不知道Mozilla编码,但现在就这样了。

根据nsIChannel::asyncOpen()

  

异步打开此频道。数据   被送到指定的流   听众,因为它变得可用。该   流监听器的方法被调用   在调用asyncOpen和的线程上   直到asyncOpen之后才调用   回报。如果asyncOpen返回   成功,渠道承诺   至少打电话给onStartRequest和   onStopRequest。

因此,当协议处理时,您可以自己实施频道对象或将其重定向到频道对象,频道的消费者使用asyncOpen()调用您的频道。由于它是异步调用,因此我们的想法是立即将控制权返回给使用者,并且假设在数据加载时调用回调函数。

我不确定我是否明白你的意思“但我似乎没有在调用AsyncOpen的线程中得到控制。”该线程由协议的使用者创建,并打开通道。

同样来自nsIChannel::asyncOpen()

  

如果asyncOpen成功返回,则   渠道负责保管   它一直活着,直到它召唤   aListener上的onStopRequest或被调用   onChannelRedirect。

由于asyncOpen向后返回控件,因此通道本身需要在某处保持活动。

如果您正在寻找示例代码,我发现codase非常有用。见nsIProtocolHandlernsIChannel。使用它我遇到view-source protocol(这种实现可能更旧,但无关紧要)。

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

这是nsViewSourceChannel的AsyncOpen:

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

无论如何,这是一个漫长而曲折的问题,你是如何创建频道的?