Доступ к изображениям и видео данным из расширения браузера (против 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);
  }
);
Другие вопросы по тегам