nsIProtocolHandler: проблема с загрузкой изображения для HTML-страницы - PullRequest
2 голосов
/ 26 июня 2009

Я создаю реализацию nsIProtocolHandler в Delphi. ( больше здесь ) И это уже работает. Данные, которые собирает модуль, передаются через nsIInputStream. У меня есть все методы и свойства nsIRequest, nsIChannel и nsIHttpChannel.

Я начал тестирование и столкнулся с чем-то странным. У меня есть страница "a.html" с этим простым HTML:

<img src="a.png">

И "xxm: //test/a.html", и "xxm: //test/a.png" работают в Firefox и предоставляют данные HTML или PNG выше. Проблема с отображением HTML-страницы, изображение не загружается. Когда я отлаживаю, я вижу:

  • NewChannel вызывается для a.png (когда Firefox обрабатывает уведомление OnDataAvailable для a.html),
  • NotificationCallbacks установлен (мне нужно только сохранить ссылку, верно?)
  • RequestHeader "Accept" имеет значение "image/png,image/*;q=0.8,*/*;q=0.5"
  • но затем объект канала освобождается (скорее всего, из-за нулевого количества ссылок)

Глядя на другие запросы, я ожидаю, что будут установлены некоторые другие свойства (например, LoadFlags или OriginalURI) и AsyncOpen для вызова, откуда я могу начать получать ответ на запрос.

Кто-нибудь это признает? Я делаю что-то неправильно? Возможно с LoadFlags или LoadGroup? Я не уверен, когда вызывать AddRequest и RemoveRequest на LoadGroup, и выглядывает из nsHttpChannel и nsBaseChannel Я не уверен, что лучше вызывать RemoveRequest рано или поздно (до или после OnStartRequest или OnStopRequest)?

Обновление: Проверено на новом Firefox 3.5, все тот же

Обновление: Чтобы попытаться еще более изолировать проблему, я пытаюсь "file: //test/a1.html" с <img src="xxm://test/a.png" /> и все еще получаю только последовательность событий, происходящих выше. Если Я должен добавить этот вторичный запрос в группу загрузки, чтобы вызвать к нему AsyncOpen, я понятия не имею, где получить ссылку на него.

Есть еще: я нахожу только один экземпляр строки "Accept", которая добавляется в заголовки запроса, она запрашивает nsIHttpChannelInternal сразу после создания нового канала, но я даже не получаю этого QueryInterface вызов через ... (я выложил здесь )

Ответы [ 2 ]

0 голосов
/ 03 июля 2009

Я думаю, что нашел (себя), внимательно посмотрите на эту страницу . Почему это не подчеркивает, что UUID был изменен по версиям, мне не ясно, но это объяснило бы, почему вещи терпят неудачу, когда (или только до) вызывает QueryInterface для nsIHttpChannelInternal.

С новым (er) UUID я получаю лучшие результаты. Как я уже упоминал в обновлении к вопросу, я разместил это на bugzilla.mozilla.org, мне интересно, если и какой ответ я получу там.

0 голосов
/ 02 июля 2009

Я снова.

Я собираюсь процитировать тот же материал из nsIChannel::asyncOpen():

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

Если вы вернетесь к nsViewSourceChannel.cpp , есть одно место, где вызывается loadGroup->AddRequest, и два места, где loadGroup->RemoveRequest вызывается.

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

и

nsViewSourceChannel::OnStopRequest(nsIRequest *aRequest, nsISupports* aContext,
                               nsresult aStatus)
{
    NS_ENSURE_TRUE(mListener, NS_ERROR_FAILURE);
    if (mChannel)
    {
        nsCOMPtr<nsILoadGroup> loadGroup;
        mChannel->GetLoadGroup(getter_AddRefs(loadGroup));
        if (loadGroup)
        {
            loadGroup->RemoveRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                    this),
                                     nsnull, aStatus);
        }
    }
    return mListener->OnStopRequest(NS_STATIC_CAST(nsIViewSourceChannel*,
                                                   this),
                                    aContext, aStatus);
}

Редактировать

Поскольку я понятия не имею о том, как работает Mozilla, я должен догадаться, прочитав какой-нибудь код. С точки зрения канала, после загрузки исходного файла его работа завершена. Если вы хотите загрузить дополнительные элементы, связанные в файле, например, изображение, вы должны реализовать это в слушателе. См. TestPageLoad.cpp . Он реализует грубый парсер и извлекает дочерние элементы при OnDataAvailable:

NS_IMETHODIMP
MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
                            nsIInputStream *stream,
                            PRUint32 offset, PRUint32 count)
{
    //printf(">>> OnDataAvailable [count=%u]\n", count);
    nsresult rv = NS_ERROR_FAILURE;
    PRUint32 bytesRead=0;
    char buf[1024];

    if(ctxt == nsnull) {
      bytesRead=0;
      rv = stream->ReadSegments(streamParse, &offset, count, &bytesRead);
    } else {
      while (count) {
        PRUint32 amount = PR_MIN(count, sizeof(buf));
        rv = stream->Read(buf, amount, &bytesRead);  
        count -= bytesRead;
      }
    }

    if (NS_FAILED(rv)) {
      printf(">>> stream->Read failed with rv=%x\n", rv);
      return rv;
    }

    return NS_OK;
}

Важно, что он вызывает streamParse(), который смотрит на атрибут src элемента img и script, и вызывает auxLoad(), который создает новый канал с новым слушателем и вызывает AsyncOpen().

uriList->AppendElement(uri);
rv = NS_NewChannel(getter_AddRefs(chan), uri, nsnull, nsnull, callbacks);
RETURN_IF_FAILED(rv, "NS_NewChannel");

gKeepRunning++;
rv = chan->AsyncOpen(listener, myBool);
RETURN_IF_FAILED(rv, "AsyncOpen");

Так как он передается в другом экземпляре MyListener объекта, он также может загружать больше дочерних элементов до бесконечности, как в ситуации с русской куклой.

...