2010-02-10 8 views
42

He estado intentando esto durante mucho tiempo y no he obtenido buenos resultados.Solicitud de redireccionamiento (nsiHttpChannel?) En las extensiones de Firefox

var myObserver = { 
    observe: function(subject, topic, data) 
    { 
     if (topic == "http-on-examine-response") 
     { 
      // implement later 
     } 
     else if(topic == "http-on-modify-request") 
     { 
      // implement later 
     } 
    }, 

    QueryInterface : function (id) 
    { 
     if (id.equals(Components.interfaces["nsIObserver"]) || 
      id.equals(Components.interfaces["nsISupports"])) 
     { 
      return this; 
     } 
     throw Components.results.NS_NOINTERFACE; 
    } 
}; 

var obs = new Service("observer-service", "ObserverService"); 
obs.addObserver(myObserver, "http-on-modify-request", false); 

Básicamente, en http-on-modify-request, sé cómo examinar el URI, averiguar qué ventana (si los hay) que está asociado con, y un montón de otras cosas. Lo que no puedo entender es cómo redirigir una solicitud, que sé que es posible desde aquí, porque puedo obtener un nsIHttpChannel antes de que se envíe alguna solicitud.

¿Alguien sabe qué hacer? :/He estado intentándolo por un par de semanas dentro y fuera, y no llegué a ninguna parte.

+0

¿Qué quiere decir con redirigir una solicitud? ¿Redirigir la ubicación del navegador a otra URL? –

+3

Sí, pero en el contexto de lo que estoy haciendo. Me di cuenta, publicaré la solución para otros más adelante si consigo hacerlo. –

+2

Esa solución probablemente sea útil. –

Respuesta

1

Tengo la impresión de que no puede hacer esto en este nivel. Probé una variedad de métodos para "engañar" externamente el código que requiere la creación de un nsIHttpChannel (ejemplo al final de la publicación).

Lo que recomendaría es que si desea una redirección, contacte la ventana de propietario del canal (que funciona el 99% del tiempo), e indíquelo a redirigir. Sé que no se comportará de la misma manera, pero como no sé exactamente por qué lo haces, esto externamente (para el usuario) parecerá estar haciendo lo mismo que lo que pides.

Aquí es lo básico de lo que estaba tratando:

if(aTopic == "http-on-examine-response") {                      
      var request = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);              

      if(!request.URI.spec.match("^http://www.apple.com/")) {               
       var ios = Components.classes["@mozilla.org/network/io-service;1"]                
        .getService(Components.interfaces.nsIIOService);                   
       var ch = ios.newChannel("http://www.apple.com/", null, null);                 

       var listener = {                            
        QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannelEventSink]),               
        onDataAvailable: function() {},                       
        onStopRequest: function() {},                        
        onStartRequest: function() {}                        
       };                                

       ch.asyncOpen(listener,null);                         

       var eventSink = request.notificationCallbacks.getInterface(Ci.nsIChannelEventSink);           
       eventSink.asyncOnChannelRedirect(request,ch,Ci.nsIChannelEventSink.REDIRECT_INTERNAL,function() {});       
      } 
3

Podemos hacer esto reemplazando el nsiHttpChannel por uno nuevo, hacer esto es un poco complicado, pero afortunadamente el add-on https-everywhere implementa este para forzar una conexión https.

https-everywhere 's código fuente está disponible here

La mayor parte del código necesario para esto se encuentra en los archivos

[IO Util.js] [ChannelReplacement.js]

podemos trabajar con los archivos anteriores solos siempre que tengamos definidas las variables básicas como Cc, Ci y la función xpcom_generateQI.

var httpRequestObserver = 
{ 
    observe: function(subject, topic, data) { 
    if (topic == "http-on-modify-request") { 

     var httpChannel = subject.QueryInterface(Components.interfaces.nsIHttpChannel);  
     var requestURL = subject.URI.spec; 

     if(isToBeReplaced(requestURL)) { 

      var newURL = getURL(requestURL);   
      ChannelReplacement.runWhenPending(subject, function() { 
        var cr = new ChannelReplacement(subject, ch); 
        cr.replace(true,null); 
        cr.open(); 
       }); 
     } 
    } 

    }, 

    get observerService() { 
    return Components.classes["@mozilla.org/observer-service;1"] 
        .getService(Components.interfaces.nsIObserverService); 
    }, 

    register: function() { 
    this.observerService.addObserver(this, "http-on-modify-request", false); 

    }, 

    unregister: function() { 
    this.observerService.removeObserver(this, "http-on-modify-request"); 

    } 
}; 


httpRequestObserver.register(); 

El código reemplazar la solicitud no redirige.

Si bien he probado el código anterior lo suficientemente bien, no estoy seguro acerca de su implementación. Hasta donde puedo llegar, copia todos los atributos del canal solicitado y los establece en el canal que se anulará. Después de lo cual, de alguna manera, la salida solicitada por la solicitud original se suministra utilizando el nuevo canal.

P.S. Había visto una publicación SO en la que se sugería este enfoque.

+0

puede proporcionar algunas funciones que faltan como isToBeReplace y ChannelReplacement – Noitidart

+0

isToBeReplace es su lógica que debería decidir si redirigir o no. ChannelReplacement se define en ChannelReplacement.js – mdprasadeng

+0

Muchas gracias @mdprasadeng por la respuesta. Me preocupaba que el mensaje no te llegara. isToBeReplace Puedo entender mucho, pero ¿puedes vincular a ChannelReplacement.js? Los enlaces anteriores están rotos – Noitidart

0

Lo he hecho de esta manera: pare nsIHttpChannel en el evento "http-on-modify-request", obtenga el objeto del navegador para la ventana actual, llame al browser.loadURI.

var utils = require("sdk/window/utils"); 

function needsRedirect(url) { 
    // to be implemented 
    return true; 
} 

function generateNewUrl(url) { 
    // to be implemented 
    return "http://www.example.com/"; 
} 

Cc["@mozilla.org/observer-service;1"] 
    .getService(Ci.nsIObserverService) 
    .addObserver({ 
     observe: function(subject, topic, data) { 
      var channel = subject.QueryInterface(Ci.nsIHttpChannel); 
      var url = channel.originalURI.spec; 
      if (needsRedirect(url)) { 
       //stop 
       channel.cancel(Cr.NS_BINDING_ABORTED); 

       //redirect 
       var gBrowser = utils.getMostRecentBrowserWindow().gBrowser; 
       var domWin = channel.notificationCallbacks.getInterface(Ci.nsIDOMWindow); 
       var browser = gBrowser.getBrowserForDocument(domWin.top.document); 
       browser.loadURI(generateNewUrl(url)); 

      } 
     } 
    }, "http-on-modify-request", false); 
+0

Parece que este código no para trabajar como se espera. Cuando aplico este código a un sitio web que tiene imágenes, esta llamada se realizará a la imagen, por lo que la página cargada se reemplazará con la URL generateNewUrl ("image.jpg") en lugar de generateNewUrl ("index.html"). Creo que 'channel.redirectTo (nsUrl)' es una mejor manera. –

0

Al probar algo que crea una versión condensada (ver this gist) de la lógica de sustitución canal mencionado en otras respuestas.

La idea general parece ser transferir todas las propiedades críticas al nuevo canal, eliminar las devoluciones de llamada del canal anterior para que las manipulaciones no interrumpan la carga de la página y luego cierren el canal anterior.

Con algunas modificaciones, se puede cambiar el URI de la página para cargas de documentos o dejarlo como está.

Advertencia: Eso fue solo un truco rápido para cargar algunas páginas, no lo he probado en profundidad y probablemente se romperá en algunos casos. Sospecho que hay razones por las que HTTPS Everywhere es más complejo.

Cuestiones relacionadas