Доступ к изображениям и видеоданным из расширения браузера (по сравнению с CORS)

Я пытаюсь написать расширение для браузера, которое выполняет некоторую обработку изображений, но мне нужен доступ к данным изображения. Мой подход состоял в том, чтобы создать скрытый элемент холста, нарисовать на нем изображения и видео с помощью drawImage, а затем прочитать данные о пикселях с помощью getImageData. Это прекрасно работает, но на многих страницах половина содержимого не отображается из-за ошибок CORS.

Я все еще не понимаю, почему существует CORS (что-то вроде запрета на кражу данных, но тогда, если данные находятся на клиентском компьютере, разве они уже не «украдены»? :S). Все, к чему это, похоже, приводит, — это заторможенные хаки, такие как внедрение JS-скриптов. Таким образом, 1. это не работает, потому что для каждого браузера слишком сложно правильно контролировать, и 2. разработчики наказываются и должны писать обходные пути для конкретного браузера. Поэтому я думаю, что у меня должно быть неправильное представление, потому что это кажется довольно глупым.

Сделав шаг назад, я думаю, что идея расширения, которое может выполнять некоторую обработку изображений, совершенно нормальна и не вредоносна, поэтому, пожалуйста, не отвечайте «нет, вам не следует делать это по соображениям безопасности». ".

Я подозреваю, что браузер рассматривает это расширение как нечто постороннее, которое может выполнять вредоносные действия. Как я могу убедить браузер в том, что клиенту нужны эти функции, и предоставить мне доступ к изображениям и видеоконтенту? У меня уже есть полный доступ к модели DOM. есть разница??

Есть ли другой способ получить данные изображения/видео из расширения?


person jozxyqk    schedule 04.03.2014    source источник
comment
В сценарии содержимого или на фоновой странице?   -  person Rob W    schedule 04.03.2014
comment
@RobW Я просто хочу знать любой прямой метод. Я не знал, что это можно сделать с фоновой страницы. Как бы вы получили доступ к данным изображения/видео на каждой странице?   -  person jozxyqk    schedule 04.03.2014
comment
@jozxyqk вы нашли решение для получения данных из видео, которое смотрит пользователь? Я столкнулся с теми же проблемами, что и вы, и я даже не могу придумать способ загрузки видео на фоновой странице, поскольку, например, видео на YouTube используют атрибут src="blob:...".   -  person krookedking    schedule 27.08.2016
comment
@krookedking Иногда помогает настройка crossOrigin = "Anonymous", но в противном случае я не видел другого способа.   -  person jozxyqk    schedule 28.08.2016
comment
@jozxyqk Хорошо, спасибо за подсказку! На самом деле я нашел другой способ добиться того, чего хотел, используя функциональность Chrome desktopCapture... довольно излишняя, но работающая.   -  person krookedking    schedule 28.08.2016


Ответы (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://stackoverflow.com/favicon.ico';

Вот минимальный файл манифеста для этой конкретной демонстрации:

{
    "name": "Get image data",
    "version": "1",
    "manifest_version": 2,
    "background": {
        "scripts": ["background.js"]
    },
    "permissions": [
        "https://stackoverflow.com/favicon.ico",
        "http://cdn.sstatic.net/stackoverflow/img/favicon.ico"
    ]
}

Второе разрешение необходимо, потому что https://stackoverflow.com/favicon.ico перенаправляет на http://cdn.sstatic.net/stackoverflow/img/favicon.ico.

Обратите внимание, что этот код не работает в скриптах содержимого, поскольку DOM является общим, и Chrome не может предлагать неограниченный исходный доступ только к сценариям содержимого.

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

var x = new XMLHttpRequest();
x.responseType = 'blob';
x.open('get', 'http://stackoverflow.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();
person Rob W    schedule 04.03.2014
comment
Спасибо за такой подробный ответ! Первый метод не кажется мне возможным, так как мне нужно добавить все изображения/видео, которые пользователь когда-либо просматривает, к разрешениям. Второй метод не затрагивает проблемы CORS? Если да, то зачем отказывать в доступе к образу DOM, если я могу получить его сам? Это было бы здорово для изображений, но видео по-прежнему остается проблемой, так как мне придется синхронизировать свою копию с той, что воспроизводится на странице. - person jozxyqk; 05.03.2014
comment
Оба метода требуют разрешений хоста. Если вам нужен доступ ко всем http(s) сайтам, просто используйте разрешение *://*/*as. CORS — это не проблема, а способ обойти ограничения политики того же происхождения. - person Rob W; 05.03.2014
comment
Спасибо @RobW за указание, что код должен быть помещен в background.js. Меня устраивает - person duckegg; 21.04.2015

Чтобы решить эту проблему, используйте функцию 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);
  }
);
person krookedking    schedule 28.08.2016