Должна ли настройка изображения src для URL данных быть доступна немедленно?

Рассмотрим следующий (хрупкий) код JavaScript:

var img = new Image;
img.src = "data:image/png;base64,..."; // Assume valid data

// Danger(?) Attempting to use image immediately after setting src
console.log( img.width, img.height );
someCanvasContext.drawImage( img, 0, 0 );

// Danger(?) Setting onload after setting src
img.onload = function(){ console.log('I ran!'); };

Вопросы)

  • Должны ли ширина и высота изображения быть правильными сразу?
  • Должен ли холст рисовать сразу?
  • Если onload обратный вызов (установленный после изменения src) будет вызван?

Экспериментальные испытания
Я создал простую тестовую страницу с похожим кодом. В Safari мои первые тесты, оба открыв HTML-страницу локально (file:/// url) и загрузив его с моего сервера, показал, что все работает: высота и ширина верны, холст работает, и onload также пожары.

В Firefox v3.6 (OS X) загрузка страницы после запуска браузера показывает, что высота / ширина неверна сразу после настройки, drawImage() выходит из строя. (The onload однако, обработчик срабатывает.) Загрузка страницы снова, однако, показывает правильность ширины / высоты сразу после установки, и drawImage() за работой. Похоже, что Firefox кэширует содержимое URL-адреса данных в виде изображения и сразу же получает его при использовании в том же сеансе.

В Chrome v8 (OS X) я вижу те же результаты, что и в Firefox: изображение не доступно сразу, но требуется некоторое время для асинхронной "загрузки" из URL-адреса данных.

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

Играя Безопасно
Для тех, кто не понимает, почему вышеупомянутое может быть опасным, знайте, что вы должны использовать изображения, подобные этим, чтобы быть в безопасности:

// First create/find the image
var img = new Image;

// Then, set the onload handler
img.onload = function(){
  // Use the for-sure-loaded img here
};

// THEN, set the src
img.src = '...';

2 ответа

Решение

Поскольку никто еще не нашел никаких спецификаций о том, как это должно себя вести, мы должны быть удовлетворены тем, как оно себя ведет. Ниже приведены результаты моих тестов.

            | данные изображения можно использовать правильно | выполняет обратный вызов onload после src
            | после настройки src?      | изменено все еще вызывается?
------------+-----------------------------+-------------------------------------
Safari  5   | да | да                
------------+-----------------------------+-------------------------------------
Chrome  8   | ** нет ** (загрузка 1-й страницы), но | да                
FireFox 3.6 | да после этого (кэшируется)    |                                     
------------+-----------------------------+-------------------------------------
IE8         | да (ограничение данных 32 КБ) | да         
------------+-----------------------------+-------------------------------------
IE9b        | да | нет ** нет **              
------------+-----------------------------+-------------------------------------

В итоге:

  • Вы не можете предполагать, что данные изображения будут доступны сразу после установки data-uri; ты должен ждать onload событие.
  • Из-за IE9 вы не можете установить onload обработчик после установки src и ожидать, что это будет вызвано.
  • Рекомендации в "Безопасной игре" (из приведенного выше вопроса) являются единственным способом обеспечения правильного поведения.

Если кто-то сможет найти спецификации, обсуждающие это, я с радостью приму их ответ.

После возврата управления движку рендеринга браузера отчеты об испытаниях true в FF 4b9 и хром 8.0.552.237. Я изменил эти части:

img.onload = function(){   // put this above img.src = …
    document.getElementById('onload').innerHTML = 'yes';
};
img.src = img_src;
…
window.setTimeout(function () {   // put this inside setTimeout
    document.getElementById('wh').innerHTML = (img.height==img.width) && (img.height==128);
}, 0);

Обновление: Да, я понимаю, что этот "ответ" больше похож на комментарий, но я просто хотел указать на него. И после тестирования я получаю воспроизводимые результаты:

  • без setTimeoutв обоих браузерах получаю false в первый раз я открываю страницу и true только после нажатия F5. После явной очистки кеша оба говорят false снова после перезагрузки.
  • с setTimeout тест ширины / высоты всегда соответствует true,

В обоих случаях изображение не появляется в первый раз, только после перезагрузки страницы.

Другие вопросы по тегам