Mam prostą implementację niestandardowego protokołu. Mówi się, że metoda newURI przyjmuje 3 argumenty (spec, charset i baseURI) i „jeśli protokół nie ma koncepcji względnych URI, trzeci parametr jest ignorowany”.

Otwieram więc stronę taką jak tada://domain/samplepage, która ma kod XML, zaczynając od tego:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Product SYSTEM "product.dtd">

Ale nie widzę żadnego żądania dotyczącego product.dtd do mojego protokołu (newURI nie jest nawet wywoływane). Czy brakuje mi czegoś w mojej implementacji? BTW: sama strona otwiera się poprawnie, ale nie ma żądania do pliku DTD.

const
    Cc = Components.classes,
    Ci = Components.interfaces,
    Cr = Components.results,
    Cu = Components.utils,
    nsIProtocolHandler = Ci.nsIProtocolHandler;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

function TadaProtocol() {
}

TadaProtocol.prototype = {
    scheme: "tada",
    protocolFlags: nsIProtocolHandler.URI_DANGEROUS_TO_LOAD,

    newURI: function(aSpec, aOriginCharset, aBaseURI) {
        let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
        uri.spec = (aBaseURI === null)
            ? aSpec
            : aBaseURI.resolve(aSpec);

        return uri;
    },

    newChannel: function(aURI) {
        let
            ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService),
            uri = ioService.newURI("chrome://my-extension/content/about/product.xml", null, null);

        return ioService.newChannelFromURI(uri);
    },

    classDescription: "Sample Protocol Handler",
    contractID: "@mozilla.org/network/protocol;1?name=tada",
    classID: Components.ID('{1BC90DA3-5450-4FAF-B6FF-F110BB73A5EB}'),
    QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
}

let NSGetFactory = XPCOMUtils.generateNSGetFactory([TadaProtocol]);
1
Dmitrii Sorin 14 luty 2012, 20:52

3 odpowiedzi

Najlepsza odpowiedź

Borys i Władimir, dziękuję!

Po pewnym czasie mam rozwiązanie. Problem polegał na tym, że plik DTD nie mógł zostać załadowany z mojego niestandardowego protokołu. Pomysł polegał na użyciu Proxy API do przesłonięcia metody schemaIs(), która została wywołana w metodzie newURI programu nsIProtocolHandler.

Więc teraz mam ten fragment kodu w metodzie newURI:

let standardUrl = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIStandardURL);
standardUrl.init(standardUrl.URLTYPE_STANDARD, -1, spec, charset, baseURI);
standardUrl.QueryInterface(Ci.nsIURL);

return Proxy.create(proxyHandlerMaker(standardUrl));

proxyHandlerMaker po prostu implementuje Proxy API i zastępuje wymaganą metodę schemeIs(). To rozwiązało problem i teraz wszystkie prośby trafiają do newChannel, gdzie możemy je obsłużyć.


Ważne notatki:

  1. Żądanie do DTD przychodzi do metody newURI() i nie dochodzi do newChannel(). To jest zachowanie domyślne. Dzieje się tak, ponieważ metoda schemaIs("chrome") jest wywoływana na obiekcie, który został zwrócony przez metodę newURI(). Ta metoda powinna zwracać "true" dla żądań DTD, jeśli chcesz, aby żądanie dotarło do metody newChannel().
  2. Metoda newChannel() jest wywoływana z obiektem {nsIURI}, który nie jest tym samym, co obiekt zwrócony przez metodę newURI.
  3. Jeśli chcesz obsługiwać oba adresy URL protokół:strona i protokół://domena/strona przez swój protokół, użyj obiektów {nsIURI} i {nsIStandardURL}
  4. Możesz przekazać utworzony obiekt {nsIStandardUrl} (standardUrl we fragmencie powyżej) jako drugi argument do funkcji Proxy.create(). To sprawi, że twój baseURI (3. argumenty w newURI) przejdzie kontrolę "baseURI instanceof nsIStandardUrl". Metoda SchemeIs() tego obiektu proxy również zwróci wartość true dla żądań plików DTD. Niestety żądania nie docierają do metody newChannel(). To może być fajne rozwiązanie problemu DTD, ale nie mogę rozwiązać tego problemu.
1
Dmitrii Sorin 16 luty 2012, 13:13

Kanał, do którego wracasz z newChannel, ma identyfikator URI chrome:// przekazany do newChannelFromURI jako swój identyfikator URI. To jest URI, który strona ma jako swój URI i jako podstawowy URI. Tak więc ładowanie DTD odbywa się bezpośrednio z „chrome://my-extension/content/about/product.dtd”.

To, co prawdopodobnie chcesz zrobić, to ustawić aURI jako oryginalny URI na kanale, do którego wracasz z nowego kanału.

1
Boris Zbarsky 14 luty 2012, 23:24

Jak wspomniał Boris w swojej odpowiedzi, implementacja protokołu nie ustawia właściwości nsIChannel.originalURI, dzięki czemu adresy URL będą rozwiązywane względem adresu URL chrome:, a nie względem adresu URL tada:. Jest jednak drugi problem z twoim kodem: w Firefoksie ładowanie zewnętrznych DTD działa tylko z adresami URL chrome:, ta kontrola jest zakodowana na stałe. Istnieje ograniczona liczba obsługiwanych DTD, które są mapowane na lokalne pliki (różne typy dokumentów HTML), ale to wszystko - Gecko nie obsługuje losowych adresów URL w <!DOCTYPE>. Aktualną logikę można zobaczyć w źródle kod. Odpowiedni błąd to błąd 22942, który nie zostanie naprawiony.

1
Wladimir Palant 15 luty 2012, 01:58