Реализация nsIProtocolHandler с Delphi - PullRequest
4 голосов
/ 14 мая 2009

Я пытаюсь построить реализацию nsIProtocolHandler в Delphi. (Ранее я успешно выполнял протокол IInternetProtocol и хочу иметь в Firefox то, что есть в Internet Explorer.) Благодаря проекту d-gecko , который каким-то образом связывает магию TInterfacedObject с магией nsISupports, я могу создать DLL, которая при запросе предоставляет nsIModule, который при запросе предоставляет nsIFactory, который предоставляет один из них. моего nsIProtocolHandler, когда его спрашивают, который предоставляет один из моих nsIChannel / nsIHttpChannel, когда его спрашивают.

При отладке с использованием firefox.exe в качестве хост-процесса я вижу, что моя библиотека загружается, три раза вызывается NewURI, вызывается NewChannel, и я передаю объект, который реализует nsIChannel и nsIHttpChannel .

Это то, где я обеспокоен. Я не должен вызывать OnStartRequest и OnDataAvailable для nsIStreamListener, который я получаю, пока не верну управление из AsyncOpen, но, похоже, я не получаю обратно управление в потоке, в котором был вызван AsyncOpen.

Я попытался отладить самодельную оболочку вокруг обработчика http по умолчанию (получено с CreateInstanceByContractID('@mozilla.org/network/protocol;1?name=http', ...). Я также завернул слушатель прошел. Как ни странно, я вижу, что OnStartRequest и OnDataAvailable получают имя после того, как моя оболочка канала умирает в том же потоке. Но кто звонит? Если это http-канал, который я пытался обернуть, как он выживает (в том же потоке) и как он получает контроль для вызова слушателя? Я сбит с толку. И застрял.

Я пытался связаться с главным разработчиком проекта d-gecko, но не получил ответа.

(Кроме того, кто-нибудь когда-нибудь замечал мою рекламу в нижней части страницы обсуждения на MDC в nsIProtocolHandler ?)

(О, еще одна вещь, да, я знаю, что «жизнь была бы проще», если бы я просто наследовал от nsBaseChannel в C ++. Но дело в том, чтобы добавить обработчик протокола FireFox в существующее ядро ​​проекта Delphi.)

Обновление: Я еще кое-что прочитал, здесь также упоминается : "Методы прослушивателя потока вызывается в потоке, который вызывает asyncOpen [...] ", но как это возможно без предварительного вызова из" хост-приложения ", мне неясно. Это трюк с XPCOM? Думаю, мне придется прочитать (много) больше источника Firefox, прежде чем я его получу.

1 Ответ

1 голос
/ 27 мая 2009

Я понятия не имею о кодировании Mozilla, но здесь все сказано.

Согласно nsIChannel :: asyncOpen () ,

Асинхронно открыть этот канал. Данные подается в указанный поток слушатель по мере поступления. методы слушателя потока вызываются в потоке, который вызывает asyncOpen и не вызывается, пока после asyncOpen возвращается. Если asyncOpen возвращает успешно канал обещает вызвать хотя бы onStartRequest и onStopRequest.

Таким образом, как протокольное средство, вы реализуете объект канала самостоятельно или перенаправляете его на объект канала, а потребитель канала вызывает ваш канал, используя asyncOpen(). Поскольку это асинхронный вызов, идея состоит в том, чтобы немедленно вернуть элемент управления потребителю, и предполагается, что он вызывает обратные вызовы при загрузке данных.

Я не уверен, понимаю ли я, что вы подразумеваете под «но мне кажется, что я не могу получить контроль над тем потоком, в котором вызывался AsyncOpen» Поток создается потребителем вашего протокола и открывает канал.

Также из nsIChannel :: asyncOpen () :

Если asyncOpen успешно возвращается, канал отвечает за хранение сам жив, пока не вызвал onStopRequest на aListener или вызывается onChannelRedirect.

Поскольку asyncOpen возвращает элемент управления обратно, сам канал должен где-то поддерживать себя.

Если вы ищете пример кода, я нашел кодазу очень полезной. См. nsIProtocolHandler и nsIChannel . Используя это, я наткнулся на протокол просмотра источника (эта реализация может быть старше, но это не имеет значения).

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

В любом случае, это долгий и извилистый способ спросить, как вы создаете свой канал?

...