我正在尝试在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源码。
答案 0 :(得分:1)
我不知道Mozilla编码,但现在就这样了。
异步打开此频道。数据 被送到指定的流 听众,因为它变得可用。该 流监听器的方法被调用 在调用asyncOpen和的线程上 直到asyncOpen之后才调用 回报。如果asyncOpen返回 成功,渠道承诺 至少打电话给onStartRequest和 onStopRequest。
因此,当协议处理时,您可以自己实施频道对象或将其重定向到频道对象,频道的消费者使用asyncOpen()
调用您的频道。由于它是异步调用,因此我们的想法是立即将控制权返回给使用者,并且假设在数据加载时调用回调函数。
我不确定我是否明白你的意思“但我似乎没有在调用AsyncOpen的线程中得到控制。”该线程由协议的使用者创建,并打开通道。
如果asyncOpen成功返回,则 渠道负责保管 它一直活着,直到它召唤 aListener上的onStopRequest或被调用 onChannelRedirect。
由于asyncOpen向后返回控件,因此通道本身需要在某处保持活动。
如果您正在寻找示例代码,我发现codase非常有用。见nsIProtocolHandler和 nsIChannel。使用它我遇到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;
}
无论如何,这是一个漫长而曲折的问题,你是如何创建频道的?