2012-01-18 43 views
5

我在這裏發佈這個問題,因爲我無法在官方Chromium擴展論壇上發佈它(或者有一個非常棒的延遲,直到它正在緩和)。我必須檢查Chromium擴展是否有附加到任意HTML元素的特定事件類型的偵聽器。在Firefox中,我可以使用以下服務來獲取這樣的信息:如何找出哪些事件監聽器類型附加到Chrome擴展中的特定HTML元素?

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

正如我在Chromium看有沒有類似的API。因此,我已經試過 以下技術(其建議here):

我創建腳本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); 

manifest.json聲明此腳本如下:

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

然後,我在以下HTML頁面上測試我的擴展:

<!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> 

不幸的是,這不起作用 - 我看不到調試器在我自定義的addEventListener()函數中停止 。我究竟做錯了什麼?

謝謝!

編輯:決賽(髒)的解決方案,這要歸功於@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); 
有一個附加的事件偵聽器將有一個特殊的屬性「_handlerTypes」,其中包含逗號分隔的事件列表

每個HTML元素。此屬性可通過Chrome擴展程序的內容腳本訪問!

+0

非常感謝您的解決方案更新! – 2013-05-22 10:58:39

回答

1

當我在單獨的獨立HTML文件上測試它時,您的腳本正常工作。它不工作作爲一個Chrome擴展,但因爲這個政策:

內容腳本執行在特殊的環境下調用一個孤立的世界。他們可以訪問他們注入的頁面的DOM,但不能訪問頁面創建的任何JavaScript變量或函數。它會查看每個內容腳本,就好像其上運行的頁面上沒有執行其他JavaScript一樣。反過來也是如此:頁面上運行的JavaScript不能調用任何函數或訪問由內容腳本定義的任何變量。 [source]

爲保證安全並避免衝突,一切都是沙盒。頁面和內容腳本之間的所有通信都必須通過DOM進行處理。


它看起來像一個人有同樣的問題,因爲你做的,做它的工作:

我解決了這個由讓我的內容腳本到 元素追加到網頁中,它看起來了DOM元素並使用jQuery的$(node).data(「events」)獲取其事件偵聽器的 。它通過將 適當的數據添加到自己的屬性來將 傳回給我的擴展。顯然,這隻適用於使用jQuery API附加事件 處理程序的頁面,但由於這是我曾經使用過的所有內容,因此這是我可以忍受的一個限制條件 。可怕的黑客。我要假裝我從來沒有寫過 。如果你有興趣: github.com/grantyb/Google-Chrome-Link-URL-Extension [source]

如果你設法使用您的events_spy.js代替$(node).data("events")您的擴展可能工作得更好。 他在頁面和內容腳本之間進行通信的方式也很醜陋。使用docs中描述的解決方案(「與嵌入頁面通信」部分)。

+0

感謝您解釋爲什麼我使用的方法不起作用!你知道我是否應該努力實現自己的目標? – spektom 2012-01-18 20:18:01

+0

目前我仍在搜索,但沒有運氣。非常酷的問題,但! – 2012-01-18 20:22:22

+0

看看這個 - http://www.quirksmode.org/js/events_advanced。html,部分'哪些事件處理程序已註冊?'。 – 2012-01-18 20:25:12

0

Chrome的控制檯現在支持getEventListeners(),例如https://code.google.com/p/accessibility-developer-tools/source/browse/src/audits/UnfocusableElementsWithOnClick.js

也就是說,我目前的使用案例是讓上面鏈接的腳本在Firefox文檔中工作,而不是在Chrome擴展中工作,這意味着我必須添加自己的事件收集黑客。我仍然在尋找一種簡單,體面的方法。如果我找到一個,我會在上面的腳本中發佈另一個補丁。 (在這一點上我想製作與Firefox支持內置和分析審計結果多了一些輔助方法叉子。)

0

花了很多時間來得到它的工作,所以我發佈的解決方案(不jQuery)...

首先,我們需要在第一次調用頁面的javascript之前注入addEventListener和removeEventListener的「修補」版本。只有在DOM內容被加載後,我們才能做到,但在解析之前我們需要它。

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

事件目標商店的修補版本訂閱的element.__eventListeners對象,它是從content_script.js inaccessable內部事件偵聽器。但是需要獲得正確的事件數量。現在訂閱事件的每個對象將具有__eventname屬性(例如:<div id="test" __click="1" __mouseover="2">,該值表示訂閱的事件偵聽器的計數)。

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

在我的解決方案,我用單獨的文件eventtarget.js,它需要被列入清單文件的web_accessable_resources部分。另請注意,run_at必須設置爲document_start才能訂閱DOMContentLoaded事件。不需要額外的權限。

manifest.json的:

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

它是如何工作的小例子:

if(document.body.getAttribute('__wheel') > 0) 
console.log('document.body subscribed to mouse wheel event'); 
相關問題