Доступ к изображениям и видео данным из расширения браузера (против CORS)
Я пытаюсь написать расширение для браузера, которое выполняет некоторую обработку изображения, но мне нужен доступ к данным изображения. Мой подход состоял в том, чтобы создать скрытый элемент холста, рисовать изображения и видео на него с помощью drawImage
, а затем прочитать данные пикселей с getImageData
, Это работает просто отлично, но на многих страницах половина содержимого отрицается ошибками CORS.
Я все еще не понимаю, почему существует CORS (что-то вроде того, чтобы не красть данные, но если данные на компьютере клиента уже не "украдены"?:S). Все, что кажется, приводит к задержанным взломам, таким как инъекция JS-скрипта. Итак, 1. это не работает, потому что это слишком сложно для каждого браузера, чтобы правильно контролировать, и 2. разработчики наказаны и должны написать обходные пути для конкретного браузера. Поэтому я думаю, что у меня неправильная идея, потому что это кажется довольно глупым.
Делая шаг назад, я думаю, что идея расширения, которое может выполнять некоторую обработку изображений, является совершенно нормальной и не злонамеренной, поэтому, пожалуйста, не отвечайте "нет, вы не должны делать это по соображениям безопасности".
Я подозреваю, что браузер рассматривает расширение как нечто чуждое, которое может пытаться совершать вредоносные действия. Как я могу заверить браузер в том, что клиенту нужны эти функции, и предоставить мне доступ к изображениям и видео? У меня уже есть полный доступ к DOM, как немного больше будет иметь значение?
Есть ли другой способ получить данные изображения / видео из расширения?
2 ответа
После добавления правильных прав доступа к файлу манифеста вы можете работать с данными из разных источников, что не мешает одной и той же политике происхождения. На фоновой странице или любой другой странице в процессе расширения вы можете получить рабочую демонстрацию описанной вами логики следующим образом:
// background.js
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var image = document.createElement('img');
image.onload = function() {
context.drawImage(image, 0, 0);
// Demo: Show image
window.open(canvas.toDataURL('image/png'));
};
image.src = 'https://stackru.com/favicon.ico';
Вот минимальный файл манифеста для этой конкретной демонстрации:
{
"name": "Get image data",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"permissions": [
"https://stackru.com/favicon.ico",
"http://cdn.sstatic.net/stackru/img/favicon.ico"
]
}
Второе разрешение необходимо, потому что https://stackru.com/favicon.ico перенаправляет на http://cdn.sstatic.net/stackru/img/favicon.ico.
Обратите внимание, что код не работает в сценариях содержимого, поскольку DOM является общим и Chrome не может предоставить неограниченный исходный доступ только к сценариям содержимого.
В зависимости от того, что вы действительно хотите, вы также можете просто попытаться получить данные изображения в необработанном виде, используя XMLHttpRequest
и обрабатывать его так, как вы хотите. Этот метод также работает в сценариях содержимого, и его преимущество в том, что он более эффективен и также сохраняет байты изображения (при использовании метода canvas изображение анализируется и обрабатывается браузером, прежде чем оно преобразуется в URL-адрес данных).,
var x = new XMLHttpRequest();
x.responseType = 'blob';
x.open('get', 'http://stackru.com');
x.onload = function() {
var fileReader = new FileReader();
fileReader.onloadend = function() {
// fileReader.result is a data-URL (string)
window.open(fileReader.result);
};
// x.response is a Blob object
fileReader.readAsDataURL(x.response);
};
x.send();
Обойти эту проблему можно с помощью Chrome desktopCapture
особенность. Обратите внимание, это означает, что пользователь должен взаимодействовать с всплывающим окном, запрашивающим разрешение каждый раз, когда вы хотите сделать запись (которую можно оставить на долгое время, но я думаю, что это не очень эффективно с точки зрения ресурсов).
Я помещаю код здесь, если это поможет.
background.js
var pending_request_id = null;
var video = document.createElement('video');
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
var theStream = null;
var CAPTURING = false;
/*
* Setting up recording when the user clicks the extension icon
*/
chrome.browserAction.onClicked.addListener(function() {
if (CAPTURING) {
console.log('stop capturing');
theStream.getVideoTracks()[0].stop();
chrome.browserAction.setIcon({path: "icons/icon128.png"});
CAPTURING = false;
} else {
pending_request_id = chrome.desktopCapture.chooseDesktopMedia(
["screen","tab"],
onAccessApproved
);
}
});
function onAccessApproved(id) {
console.log('onAccessApproved: ' +id)
if (!id) {
console.log("Access rejected.");
return;
}
navigator.webkitGetUserMedia({
audio:false,
video: {
mandatory: {
chromeMediaSource: "desktop",
chromeMediaSourceId: id,
maxWidth: 4000,
maxHeight: 4000
}
}
}, gotStream, getUserMediaError);
}
function getUserMediaError(error) {
console.log("getUserMedia() failed:", error);
}
function gotStream(stream) {
console.log("gotStream", stream);
theStream = stream;
video.src = URL.createObjectURL(theStream);
video.play();
}
video.addEventListener('loadedmetadata',function(){
console.log('loadedmetadata')
chrome.browserAction.setIcon({path: "icons/icon128_recording.png"});
CAPTURING = true;
}, false);
/*
* You can trigger this when you want; I am doing it on every mouse move over the video (triggered by the content_script.js)
*/
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (!CAPTURING) {
console.log('User needs to activate the recording of the screen')
return false; // no response
}
ctx.drawImage(video, 0, 0);
var url = canvas.toDataURL('image/png');
window.open(url);
}
);