2012-01-18 20 views
5

Estoy publicando esta pregunta aquí, ya que no puedo publicarla en los foros oficiales de extensión de Chromium (o hay una tremenda demora hasta que se modera). Tengo que verificar en la extensión de Chromium si hay un oyente de un tipo de evento específico adjunto a un elemento HTML arbitrario. En Firefox que podría utilizar el siguiente servicio para obtener esta información:¿Cómo averiguar qué tipos de escucha de eventos se unen a un elemento HTML específico en la extensión de Chrome?

var listenerService = Components.classes["@mozilla.org/eventlistenerservice;1"] 
      .getService(Components.interfaces.nsIEventListenerService); 
var infos = listenerService.getListenerInfoFor(element, {}); 
var types = []; 
for (var i = 0; i < infos.length; ++i) { 
    var info = infos[i].QueryInterface(Components.interfaces.nsIEventListenerInfo); 
    types.push(info.type); 
} 

Como veo en Chromium no hay API similar. Por lo tanto, he probado el siguiente técnica (que fue sugerido here):

He creado guión events_spy.js:

(function(original) { 
    Element.prototype.addEventListener = function(type, listener, useCapture) { 
    if (typeof (this._handlerTypes) == 'undefined') { 
     this._handlerTypes = {}; 
    } 
    this._handlerTypes[type] = true; 
    return original.apply(this, arguments); 
    } 
})(Element.prototype.addEventListener); 

(function(original) { 
    Element.prototype.removeEventListener = function(type, listener,useCapture) { 
    if (typeof (this._handlerTypes) != 'undefined') { 
     delete this._handlerTypes[type]; 
    } 
    return original.apply(this, arguments); 
    } 
})(Element.prototype.removeEventListener); 

Declaro este script en manifest.json de la siguiente manera:

"content_scripts" : [{ 
    "matches" : [ "http://*/*", "https://*/*" ], 
    "js" : [ "content/events_spy.js" ], 
    "run_at" : "document_start", 
    "all_frames" : true 
}, 
... 
] 

entonces, pongo a prueba mi extensión en la página HTML siguiente:

<!DOCTYPE html> 
<html> 
<head> 
</head> 
<body> 
    <a id="test" href="#">Click here</a> 
    <script type="application/javascript"> 
     document.getElementById("test").addEventListener("click", function() 
{ alert("clicked"); }, false); 
    </script> 
</body> 
</html> 

Por desgracia, esto no funciona - no puedo ver que depurador se detiene en el interior de mi función personalizada addEventListener(). ¿Qué estoy haciendo mal?

Gracias!

EDITAR: Solución final (sucio), gracias a @kdzwinel

var injectedJS = "\ 
(function(original) { \ 
    Element.prototype.addEventListener = function(type, listener, useCapture) { \ 
    var attr = this.getAttribute('_handlerTypes'); \ 
    var types = attr ? attr.split(',') : []; \ 
    var found = false; \ 
    for (var i = 0; i < types.length; ++i) { \ 
     if (types[i] == type) { \ 
     found = true; \   
     break; \     
     } \    
    } \   
    if (!found) { \ 
     types.push(type); \ 
    } \   
    this.setAttribute('_handlerTypes', types.join(',')); \ 
    return original.apply(this, arguments); \ 
    } \ 
})(Element.prototype.addEventListener); \ 
\ 
(function(original) { \ 
    Element.prototype.removeEventListener = function(type, listener, useCapture) { \ 
    var attr = this.getAttribute('_handlerTypes'); \ 
    var types = attr ? attr.split(',') : []; \ 
    var removed = false; \ 
    for (var i = 0; i < types.length; ++i) { \ 
     if (types[i] == type) { \ 
     types.splice(i, 1); \ 
     removed = true; \  
     break; \     
     } \    
    } \   
    if (removed) { \ 
     this.setAttribute('_handlerTypes', types.join(',')); \ 
    } \   
    return original.apply(this, arguments); \ 
    } \ 
})(Element.prototype.removeEventListener); \ 
"; 

var script = document.createElement("script"); 
script.type = "text/javascript"; 
script.appendChild(document.createTextNode(injectedJS)); 
document.documentElement.appendChild(script); 
elemento

Cada HTML que tiene un detectores de eventos asociados tendrán un atributo especial "_handlerTypes", que contiene una lista separada por comas de los acontecimientos . ¡Y este atributo es accesible desde el script de contenido de la extensión de Chrome!

+0

Muchas gracias por la actualización de su solución! –

Respuesta

1

Su secuencia de comandos funciona bien cuando lo pruebo en un solo archivo, HTML independiente. Aunque no funciona como una extensión de Chrome debido a esta política:

Las secuencias de comandos de contenido se ejecutan en un entorno especial llamado mundo aislado. Tienen acceso al DOM de la página en la que se insertan, pero no a las variables o funciones de JavaScript creadas por la página. Mira a cada script de contenido como si no hubiera otro JavaScript ejecutándose en la página en la que se está ejecutando. Lo mismo ocurre a la inversa: JavaScript que se ejecuta en la página no puede invocar ninguna función ni acceder a ninguna variable definida por las secuencias de comandos de contenido. [source]

Todo es un espacio aislado para mayor seguridad y para evitar conflictos. Toda la comunicación entre la página y el script de contenido se debe manejar a través de DOM.


Parece que alguien tenía el mismo problema que tú haces y lo hizo funcionar:

He resuelto esto consiguiendo mi guión de contenido para añadir un elemento en la página, que busca la Elemento DOM y obtiene sus oyentes de eventos utilizando jQuery's $ (node) .data ("events"). Comunica a mi extensión añadiendo un atributo a sí mismo con los datos apropiados .Claramente esto solo funciona en páginas que adjuntan controladores de eventos utilizando la API de jQuery, pero como eso es todo lo que uso, es una limitación de con la que puedo vivir. Horrible pirateo. Voy a pretender que nunca lo escribí . Si está interesado: github.com/grantyb/Google-Chrome-Link-URL-Extension [source]

Su extensión puede funcionar incluso mejor si logras usar su events_spy.js en lugar de $(node).data("events"). También la forma en que se comunica entre la página y el script de contenido es fea. Use la solución descrita en el docs (sección 'Comunicación con la página de incrustación').

+0

¡Gracias por la explicación de por qué el método que uso no funciona! ¿Sabes si hay algo que debería intentar para lograr mi objetivo? – spektom

+0

Todavía estoy buscando pero no tengo suerte hasta ahora. ¡Muy buena pregunta! –

+0

Mira esto - http://www.quirksmode.org/js/events_advanced.html, sección '¿Qué controladores de eventos están registrados?'. –

0

La consola de Chrome ahora es compatible con getEventListeners(), consulte https://code.google.com/p/accessibility-developer-tools/source/browse/src/audits/UnfocusableElementsWithOnClick.js para ver un ejemplo.

Dicho esto, mi actual caso de uso es lograr que el script vinculado anteriormente funcione en un documento de Firefox en lugar de en una extensión de Chrome, lo que significa que tendré que agregar mis propios hacks de recopilación de eventos. Todavía estoy buscando un enfoque simple y decente. Si encuentro uno, publicaré otro parche en el guión anterior. (En este punto, estoy pensando en hacer un fork con el soporte de Firefox incorporado y algunos métodos más de ayuda para analizar los resultados de la auditoría.)

0

Pasé mucho tiempo hasta que funcionó, publicando mi solución (sin jQuery) ...

Primero, necesitamos inyectar la versión "parcheada" de addEventListener y removeEventListener antes de la primera invocación de javascript de la página. Podemos hacerlo solo después de cargar el contenido de DOM, pero lo necesitamos antes de analizarlo.

content_script.js:

window.addEventListener('DOMContentLoaded', function() { 
    var script = document.createElement('script'); 
    script.setAttribute('type', 'text/javascript'); 
    script.setAttribute('src', chrome.extension.getURL('eventtarget.js')); 
    // injected <script> tag must be parsed first! 
    document.head.insertBefore(script, document.head.firstChild); 
}); 

versión Patched de tiendas EventTarget suscritos detectores de eventos dentro del objeto element.__eventListeners, que es inaccesible desde content_script.js. Pero es necesario para obtener la cantidad correcta de eventos. Cada objeto suscrito en evento ahora tendrá el atributo __eventname (por ejemplo: <div id="test" __click="1" __mouseover="2">, el valor indica el recuento de oyentes de eventos suscritos).

eventtarget.js:

EventTarget.prototype.__eventListeners = []; 

EventTarget.prototype.__addEventListener = EventTarget.prototype.addEventListener; 
EventTarget.prototype.addEventListener = function() { 
    var found = false; 
    if(!this.__eventListeners[arguments[0]]) 
    this.__eventListeners[arguments[0]] = []; 
    var key; 
    for(key in this.__eventListeners[arguments[0]]) { 
    found = this.__eventListeners[arguments[0]][key] === arguments[1]; 
    if(found) 
    break; 
    } 
    if(!found) 
    this.__eventListeners[arguments[0]].push(arguments[1]); 
    if(this.setAttribute) 
    this.setAttribute('__'+arguments[0], this.__eventListeners[arguments[0]].length); 
    return(this.__addEventListener.apply(this, arguments)); 
} 

EventTarget.prototype.__removeEventListener = EventTarget.prototype.removeEventListener; 
EventTarget.prototype.removeEventListener = function() { 
    var found = false; 
    if(!this.__eventListeners[arguments[0]]) 
    this.__eventListeners[arguments[0]] = []; 
    var key; 
    for(key in this.__eventListeners[arguments[0]]) { 
    found = this.__eventListeners[arguments[0]][key] === arguments[1]; 
    if(found) 
    break; 
    } 
    if(found) 
    this.__eventListeners[arguments[0]].splice(key, 1); 
    if(this.setAttribute) 
    this.setAttribute('__'+arguments[0], this.__eventListeners[arguments[0]].length); 
    return(this.__removeEventListener.apply(this, arguments)); 
} 

En mi solución que uso eventtarget.js archivo separado, que se necesita para ser incluidos en la sección web_accessable_resources del archivo de manifiesto. También tenga en cuenta que run_at debe establecerse en document_start para suscribirse al evento DOMContentLoaded. No se requieren permisos adicionales.

manifest.json:

... 
"web_accessible_resources": ["eventtarget.js"], 
"content_scripts": [ 
    { 
    "matches": ["<all_urls>"], 
    "js": ["content_script.js"], 
    "run_at": "document_start", 
    "all_frames": true 
    } 
], 
... 

pequeño ejemplo de cómo funciona:

if(document.body.getAttribute('__wheel') > 0) 
console.log('document.body subscribed to mouse wheel event'); 
Cuestiones relacionadas