簡體   English   中英

CustomEvent.detail是否“污染”?

[英]CustomEvent.detail “tainted”?

我正在開發Chrome擴展程序,以增加網站的便利性。
我可以訪問頁面的DOM,但是我還需要與該頁面上的“第一方” JS進行交互,而我無法通過其擴展程序進行此操作。

我可以在頁面中注入任意標簽(最著名的是<script>標簽),但是由於轉義了像

{
    html: '<div onclick="doSomething(this, \'someName\')"></div>'
}

是一個真正的痛苦,我想將注入的代碼保持在絕對最低限度。

我嘗試將事件偵聽器注入頁面中,以便從頁面中獲取JS變量,但是遇到了問題。
看來,如果CustomEvent從擴展名傳遞到網站或返回,又或者CustomEvent.detail在某處包含某些類型的對象(至少是函數和錯誤),則將清除整個CustomEvent.detail ,即設置為null。

腳本(extension.js):

(function()
{
    var script = document.createElement('script');
    script.innerHTML = [
"window.addEventListener('xyz', function(ev)",
"    {                                      ",
"        console.log('after dispatch:');    ",
"        console.log(ev.detail);            ",
"    });                                    ",
    ].join('\n');
    document.head.appendChild(script);
    // JSON-serializable data
    var e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger' } });
    console.log('before dispatch:')
    console.log(e.detail);
    window.dispatchEvent(e);
    // non-JSON-serializable data
    var detail = { x: 42, name: 'Schroedinger' };
    detail.detail = detail; // Create circular reference
    e = new CustomEvent('xyz', { detail: detail });
    console.log('before dispatch:')
    console.log(e.detail);
    window.dispatchEvent(e);
    // data with function
    e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', func: function(){} } });
    console.log('before dispatch:');
    console.log(e.detail);
    window.dispatchEvent(e);
    // data with error object
    e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', err: new Error() } });
    console.log('before dispatch:');
    console.log(e.detail);
    window.dispatchEvent(e);
})();

輸出(為便於閱讀而分類):

before dispatch:
Object {x: 42, name: "Schroedinger"}
after dispatch:
Object {x: 42, name: "Schroedinger"}

before dispatch:
Object {x: 42, name: "Schroedinger", detail: Object}
after dispatch:
Object {x: 42, name: "Schroedinger", detail: Object}

before dispatch:
Object {x: 42, name: "Schroedinger", func: function (){}}
after dispatch:
null

before dispatch:
Object {x: 42, name: "Schroedinger", err: Error at chrome-extension://...}
after dispatch:
null

我最初以為JSON可序列化是問題所在,但是循環引用在事件中傳遞得很好,如果JSON序列化會破壞循環引用。
感覺某些對象“污染”事件的細節與非跨域圖像污染畫布的方式相同,只是控制台中什么也沒有。

我找不到有關此行為的任何文檔,並且(如Paul S.建議的那樣), Chrome權限列表上似乎沒有該權限的“特權”。

已在Chrome 40.0.2214.115m,43.0.2357.124m和48.0.2547.0-dev中測試。

我發現了什么

我最初以為這是一種安全功能,主要是因為Firefox的行為方式如此。

通過將事件偵聽器放置在可以通過mozIJSSubScriptLoader加載的單獨文件中,在Firefox中進行了等效測試:

test.js:

 (function() { window.addEventListener('xyz', function(ev) { console.log('after dispatch:'); console.log(ev.detail); }); })(); 

firefox.js:

 (function() { var mozIJSSubScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader); window.addEventListener('load', function load(event) { window.removeEventListener('load', load); window.gBrowser.addEventListener('DOMContentLoaded', function(event) { mozIJSSubScriptLoader.loadSubScript('chrome://my-extension/content/test.js', window.content, 'UTF-8'); // JSON-serializable data var e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger' } }); console.log('before dispatch:') console.log(e.detail); window.content.dispatchEvent(e); // non-JSON-serializable data e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', func: function(){} } }); console.log('before dispatch:'); console.log(e.detail); window.content.dispatchEvent(e); }); }); })(); 

結果:

控制台日志

(請注意該錯誤發生兩次。)

因此,在Firefox中,甚至包含什么detail都不重要-只要它來自擴展程序,就不允許該頁面訪問它。
在我看來,這是一項安全功能。

我將以上內容引用為引號的原因是,這在Chrome中有些不同!

經過更深入的研究后,盡管擴展名和頁面共享DOM樹,但它們存在於兩個不同的上下文中。
我不知道這實際上是安全功能還是技術上的后果,但這當然會導致只能來回傳遞可克隆對象的結果。

不過,令我感到困惑的是,該操作默默地失敗了,根據HTML標准§2.7.5(結構化克隆) ,整個操作應該失敗並出現錯誤:

input如果輸入是另一種本機對象類型(例如,錯誤,函數)
input如果輸入是宿主對象(例如DOM節點)
引發DataCloneError異常並中止整體結構化克隆算法。

解決方法

我最終使用了一個相當簡單(盡管不是很漂亮)的解決方法:
在Chrome瀏覽器中,沒有等效於mozIJSSubScriptLoader ,但是您可以在擴展程序內將<script>標記附加到頁面上(不允許在FF中執行此操作)。
chrome.extension.getURL一起,可用於在頁面上下文中運行與擴展名打包在一起的JS文件:

(function()
{
    var script = document.createElement('script');
    script.src = chrome.extension.getURL('extension.js');
    document.head.appendChild(script);
})();

當然需要

"web_accessible_resources": [ "extension.js" ]

是在manifest.json中設置的,雖然不漂亮,但這不是實際問題。

當然,這樣做的缺點是,在extension.js您不再有權訪問您的擴展程序可以訪問的任何chrome API,但就我而言,我並不需要它。 它不會是太難通過設立一個代理CustomEvent的,雖然,作為Chrome的API中最重要的部分只需要返回的數據可克隆的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM