2012-01-18 25 views
5

Tôi đang đăng câu hỏi này ở đây, vì tôi không thể đăng câu hỏi trên diễn đàn mở rộng chính thức của Chromium (hoặc có sự chậm trễ tuyệt vời cho đến khi nó được kiểm duyệt). Tôi phải kiểm tra tiện ích mở rộng của Chromium cho dù có người nghe loại sự kiện cụ thể được đính kèm với phần tử HTML tùy ý hay không. Trong Firefox, tôi có thể sử dụng dịch vụ sau để nhận thông tin này:Cách tìm hiểu loại trình nghe sự kiện nào được gắn với phần tử HTML cụ thể trong tiện ích mở rộng của 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); 
} 

Như tôi thấy trong Chromium không có API tương tự. Vì vậy, tôi đã thử các kỹ thuật sau đây (được đề nghị here):

tôi đã tạo ra kịch bả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); 

Tôi tuyên bố kịch bản này trong manifest.json như sau:

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

Sau đó, tôi thử nghiệm tiện ích mở rộng của mình trên trang HTML sau:

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

Thật không may, tính năng này không hoạt động - Tôi không thể thấy trình gỡ lỗi dừng lại bên trong chức năng tùy chỉnh addEventListener() của tôi. Tôi đang làm gì sai?

Cảm ơn!

EDIT: Final (bẩn) giải pháp, nhờ @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); 
yếu tố

Mỗi HTML mà có nghe sự kiện đính kèm sẽ có một thuộc tính đặc biệt "_handlerTypes", trong đó có chứa dấu phẩy tách ra danh sách các sự kiện . Và thuộc tính này có thể truy cập được từ tập lệnh nội dung của tiện ích mở rộng của Chrome!

+0

Cảm ơn rất nhiều cho việc cập nhật với giải pháp của bạn! –

Trả lời

1

Tập lệnh của bạn hoạt động tốt khi tôi thử nghiệm trên một tệp HTML độc lập. Tuy nhiên, tiện ích này không hoạt động như tiện ích mở rộng của Chrome vì chính sách này:

Đoạn mã nội dung được thực thi trong một môi trường đặc biệt được gọi là thế giới bị cô lập. Họ có quyền truy cập vào DOM của trang mà họ được tiêm vào, nhưng không truy cập vào bất kỳ biến hoặc hàm JavaScript nào được tạo bởi trang. Dường như mỗi tập lệnh nội dung như thể không có JavaScript thực thi khác trên trang đang chạy. Điều ngược lại cũng đúng: JavaScript chạy trên trang không thể gọi bất kỳ chức năng nào hoặc truy cập bất kỳ biến nào được xác định bởi tập lệnh nội dung. [source]

Mọi thứ được hộp cát để bảo mật và tránh xung đột. Tất cả thông tin liên lạc giữa trang và tập lệnh nội dung phải được xử lý thông qua DOM.


Có vẻ như ai đó đã có vấn đề tương tự như bạn làm và làm cho nó hoạt động:

tôi giải quyết điều này bằng cách nhận được kịch bản nội dung của tôi để nối thêm một yếu tố vào trang, trông lên Phần tử DOM và tìm nạp các trình nghe sự kiện của nó bằng cách sử dụng $ (nút) .data của jQuery ("sự kiện"). Nó liên lạc quay lại tiện ích mở rộng của tôi bằng cách thêm thuộc tính vào chính nó với dữ liệu thích hợp .Rõ ràng điều này chỉ hoạt động trên các trang đính kèm sự kiện xử lý sự kiện bằng API jQuery, nhưng vì đó là tất cả những gì tôi từng sử dụng, đó là giới hạn tôi có thể sống với. Awful hack. Tôi sẽ giả vờ tôi không bao giờ đã viết nó. Nếu bạn quan tâm: github.com/grantyb/Google-Chrome-Link-URL-Extension [source]

phần mở rộng của bạn có thể làm việc tốt hơn nếu bạn quản lý để sử dụng của bạn events_spy.js thay vì $(node).data("events"). Ngoài ra, cách giao tiếp giữa trang và tập lệnh nội dung rất xấu. Sử dụng giải pháp được mô tả trong phần docs (phần 'Giao tiếp với trang nhúng').

+0

Cảm ơn bạn đã giải thích lý do tại sao phương pháp tôi sử dụng không hoạt động! Bạn có biết có điều gì tôi nên cố gắng đạt được mục tiêu của mình không? – spektom

+0

Tôi vẫn đang tìm kiếm nhưng không may mắn cho đến nay. Câu hỏi rất hay! –

+0

Kiểm tra điều này - http://www.quirksmode.org/js/events_advanced.html, phần 'Trình xử lý sự kiện nào được đăng ký?'. –

0

Bàn điều khiển của Chrome hiện hỗ trợ getEventListeners(), xem https://code.google.com/p/accessibility-developer-tools/source/browse/src/audits/UnfocusableElementsWithOnClick.js để biết ví dụ.

Điều đó nói rằng, trường hợp sử dụng hiện tại của tôi là nhận tập lệnh được liên kết ở trên để làm việc trong tài liệu Firefox chứ không phải tiện ích mở rộng của Chrome, có nghĩa là tôi sẽ phải thêm các tập hợp thu thập sự kiện của riêng mình. Tôi vẫn đang tìm kiếm một cách tiếp cận đơn giản, phong nha. Nếu tôi tìm thấy một, tôi sẽ đăng một bản vá khác cho kịch bản trên. (Tại thời điểm này tôi đang nghĩ đến việc tạo một ngã ba với hỗ trợ Firefox tích hợp và một vài phương thức trợ giúp khác để phân tích kết quả kiểm toán.)

0

Đã dành nhiều thời gian để làm việc, vì vậy hãy xuất bản giải pháp của tôi (không có jQuery) ...

Trước hết, chúng ta cần tiêm phiên bản "vá" của addEventListener và removeEventListener trước cuộc gọi đầu tiên của javascript của trang. Chúng tôi chỉ có thể thực hiện việc này sau khi nội dung DOM được tải, nhưng chúng tôi cần nó trước khi được phân tích cú pháp.

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

phiên bản Patched của các cửa hàng EventTarget đăng ký nghe sự kiện bên trong đối tượng element.__eventListeners, đó là inaccessable từ content_script.js. Nhưng cần phải có số lượng sự kiện phù hợp. Mỗi đối tượng được đăng ký trên sự kiện sẽ có thuộc tính __eventname (ví dụ: <div id="test" __click="1" __mouseover="2">, giá trị cho biết số lượng người nghe sự kiện đã đăng ký).

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

Trong giải pháp của tôi sử dụng tập tin riêng biệt eventtarget.js, nó cần phải được bao gồm trong phần web_accessable_resources của file manifest. Cũng xin lưu ý rằng, run_at phải được đặt thành document_start để đăng ký sự kiện DOMContentLoaded. Không có quyền bổ sung là cần thiết.

manifest.json:

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

ít ví dụ về cách thức hoạt động:

if(document.body.getAttribute('__wheel') > 0) 
console.log('document.body subscribed to mouse wheel event'); 
Các vấn đề liên quan