4

Delphi で nsIProtocolHandler 実装を構築しようとしています。(私は以前に IInternetProtocol を成功させたことがあり、Internet Explorer で得たものを FireFox で使用したいと考えています。) d-geckoプロジェクトのおかげで、何らかの方法で TInterfacedObject マジックを nsISupports マジックにリンクすることができます。要求されたときに nsIModule を提供し、要求されたときに nsIFactory を提供し、要求されたときに nsIProtocolHandler の 1 つを提供し、要求されたときに nsIChannel/nsIHttpChannel の 1 つを提供する DLL を作成します。

ホスト プロセスとして firefox.exe を使用してデバッグすると、ライブラリが読み込まれ、NewURI が 3 回呼び出され、NewChannel が呼び出され、nsIChannel とnsIHttpChannelを実装するオブジェクトが渡されることがわかります。

悩んでいるところです。AsyncOpen から制御を返すまで、取得した nsIStreamListener で OnStartRequest と OnDataAvailable を呼び出すことは想定されていませんが、AsyncOpen が呼び出されたスレッドで制御を取得していないようです。

CreateInstanceByContractID('@mozilla.org/network/protocol;1?name=http',デフォルトのhttpハンドラー( ...で取得)の周りに自作のラッパーを使用してデバッグを試みました。渡されたリスナーもラップしました。奇妙なことに、チャネル ラッパーが終了した後、同じスレッドでOnStartRequest と OnDataAvailable が呼び出されます。しかし、誰が電話しているのですか?それがラップしようとしていた http チャネルである場合、どのように (同じスレッドで) 生き残り、リスナーを呼び出すための制御をどのように取得しますか? 私は困惑しています。そして立ち往生。

d-gecko プロジェクトの主な開発者に連絡を取ろうとしましたが、応答がありません。

(また、誰かがnsIProtocolHandler の MDC のトーク ページの下部にある私の宣伝文句に気付きましたか?)

(もう 1 つ、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 はコントロールをすぐに返すため、チャネル自体はどこかで生き続ける必要があります。

サンプル コードを探している場合は、codase が非常に便利であることがわかりました。nsIProtocolHandlernsIChannelを参照してください。それを使用して、 view-source プロトコルに出会いました(この実装は古いかもしれませんが、問題ではありません)。

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 に答える