Поддерживает ли chrome.runtime отправку сообщений с переносимыми объектами?

Я изучаю переносимые объекты: http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast

Они выглядят довольно круто, и я хотел бы использовать их в своем расширении, чтобы ускорить передачу данных изнутри iframe наружу из iframe.

У меня есть пример кода, работающего, но он использует Web Worker:

var s = document.createElement("script");
s.type = "text/js-worker";
s.innerHTML = 'console.log("i ran!");';
document.body.appendChild(s);

var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) {
    return oScript.textContent;
}), { type: "text/javascript" });

var worker = new Worker(window.URL.createObjectURL(blob));

var arrayBuffer = new ArrayBuffer(1);

worker.onmessage = function (oEvent) {
    console.log("Called back by the worker!\n");
};

console.log("worker:", worker);
worker.postMessage(arrayBuffer, [arrayBuffer]);

if (arrayBuffer.byteLength) {
    console.error("nope");
} else {
    console.log("it worked");
}

У кого-нибудь есть какая-либо информация о поддержке или временная шкала для поддержки / crbug для использования порта, например:

var port = chrome.runtime.connect({
    name: 'youTubeIFrameConnectRequest'
});

//port.postMessage -- transferrable object.

Я не вижу никакой поддержки или что-либо о поддержке, но это кажется действительно странным!

1 ответ

Решение

Сообщения, которые проходят через API передачи сообщений расширения, всегда сериализуются JSON. Этот формат используется не только для передачи сообщений между фоновой страницей и скриптами содержимого, но также и для собственных приложений. Итак, я думаю, что не очень вероятно, что API передачи сообщений поддерживают больше элементов.

Запрос на алгоритм структурированного клонирования (более мощный, чем JSON-сериализация, менее мощный, чем передаваемые) был запрошен еще в 2012 году ( выпуск Chromium 112163). Вопрос все еще открыт; кто-то предложил использовать SharedWorker как "батут".

SharedWorker зависит от той же политики происхождения, поэтому вызывающие абоненты должны находиться в одном источнике. Для этого вы можете добавить страницу в web_accessible_resources и вставьте эту страницу в рамку.

В конце этого ответа я прикрепил голую реализацию батута. Создайте расширение с этими файлами. Затем откройте вкладку. Эта вкладка будет содержать встроенный фрейм, а демонстрационная версия отправит сообщение общему работнику. Это сообщение будет перенесено на фоновую страницу, просто просмотрите консоль фоновой страницы, чтобы увидеть эти сообщения.
Демо-версия минимальна, вам нужно реализовать управление портами (уничтожение) самостоятельно.
Демонстрация не использует передачу передаваемых сообщений (пока), потому что это общая реализация, которая допускает несколько портов. Если вы гарантируете, что одновременно существует не более двух портов, то вы можете изменить код для использования переносимых (переносимые имеют смысл только тогда, когда есть один полученный и один отправитель, потому что право собственности на объект также передается).

Особый случай: тот же процесс

Если весь ваш код выполняется в одном и том же процессе, вы можете использовать более простой подход без SharedWorker s.

Та же политика происхождения запрещает прямой доступ из / в кадр и расширение, поэтому вы будете использовать parent.postMessage пересечь этот мост. Затем в событии onmessage страницы вы можете использовать chrome.extension.getViews чтобы получить прямую ссылку на window объект одной из ваших страниц расширения (например, всплывающая страница, страница настроек,...).
С других страниц chrome.extension.getBackgroundPage() дает ссылку на window объект фоновой страницы (для страницы события используйте chrome.runtime.getBackroundPage(callback)).

Если вы хотите соединить два фрейма, используйте API обмена сообщениями в канале (см. Спецификацию whatwg и статью Opera). С помощью этого метода вы создадите прямую связь между фреймами, даже если они расположены в разных источниках!

Пример: батут

worker.js

var ports = [];
onconnect = function(event) {
    var port = event.ports[0];
    ports.push(port);
    port.start();
    port.onmessage = function(event) {
        for (var i = 0; i < ports.length; ++i) {
            if (ports[i] != port) {
                ports[i].postMessage(event.data);
            }
        }
    };
};

trampoline.js

var worker = new SharedWorker(chrome.runtime.getURL('worker.js'));
worker.port.start();
// Demo: Print the message to the console, and remember the last result
worker.port.onmessage = function(event) {
    console.log('Received message', event.data);
    window.lastMessage = event.data;
};
// Demo: send a message
worker.port.postMessage('Hello');

trampoline.html

<script src="trampoline.js"></script>

contentscript.js

var f = document.createElement('iframe');
f.src = chrome.runtime.getURL('trampoline.html');
f.hidden = true;
(document.body || document.documentElement).appendChild(f);

manifest.json

Примечание: я положил trampoline.js в качестве фонового скрипта для экономии места в этом ответе. С точки зрения веб-работника, не имеет значения, кто инициировал сообщение, поэтому я повторно использовал код для отправки и получения сообщений (в конце концов, это простая демонстрация!).

{
    "name": "Trampoline demo",
    "version": "1",
    "manifest_version": 2,
    "background": {
        "scripts": ["trampoline.js"],
        "persistent": true
    },  
    "content_scripts": [{
        "js": ["contentscript.js"],
        "matches": ["<all_urls>"]
    }],
    "web_accessible_resources": [
        "trampoline.html"
    ]   
}
Другие вопросы по тегам