Sollte das Setzen eines Bild-src auf eine Daten-URL sofort verfügbar sein?

74

Betrachten Sie den folgenden (fragilen) JavaScript-Code:

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!'); };

Die Fragen)

  • Sollte die Bildbreite und -höhe sofort korrekt sein?
  • Sollte die Leinwandzeichnung sofort funktionieren?
  • Sollte der onloadRückruf (eingestellt nach dem Ändern des src) aufgerufen werden?

Experimentelle Tests
Ich habe eine einfache Testseite mit ähnlichem Code erstellt. In Safari haben meine ersten Tests, sowohl beim lokalen Öffnen der HTML-Seite ( file:///URL) als auch beim Laden von meinem Server, gezeigt, dass alles funktioniert: Die Höhe und Breite sind korrekt, das Zeichnen der Leinwand funktioniert und das wird onloadauch ausgelöst.

In Firefox v3.6 (OS X) zeigt das Laden der Seite nach dem Starten des Browsers, dass die Höhe / Breite unmittelbar nach dem Einstellen nicht korrekt ist drawImage(). (Der onloadHandler wird jedoch ausgelöst.) Beim erneuten Laden der Seite wird jedoch angezeigt, dass die Breite / Höhe unmittelbar nach dem Einstellen und drawImage()Arbeiten korrekt ist . Es scheint, dass Firefox den Inhalt der Daten-URL als Bild zwischenspeichert und sofort verfügbar hat, wenn er in derselben Sitzung verwendet wird.

In Chrome v8 (OS X) werden dieselben Ergebnisse wie in Firefox angezeigt: Das Image ist nicht sofort verfügbar, es dauert jedoch einige Zeit, bis es asynchron von der Daten-URL "geladen" wird.

Zusätzlich zum experimentellen Beweis, welche Browser die oben genannten Funktionen ausführen oder nicht, würde ich gerne Links zu Spezifikationen verwenden, wie sich dies verhalten soll. Bisher war mein Google-Fu der Aufgabe nicht gewachsen.

Auf Nummer sicher gehen
Wenn Sie nicht verstehen, warum das oben Genannte gefährlich sein kann, sollten Sie aus Sicherheitsgründen solche Bilder verwenden:

// 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 = '...';
Phrogz
quelle
1
Sie müssen auf jeden Fall einstellen, onloadbevor Sie das einstellen src. Wenn das Ladeereignis jedoch manchmal in der aktuellen Konstellation ausgelöst wird, ist das Festlegen des URI "Daten" tatsächlich asynchron - was mich überraschen würde. Interessant!
Pekka
Ist es nicht sicherer anzunehmen, dass nein, eine Zeitüberschreitung zu pumpen und fortzufahren, nachdem der Browser die Möglichkeit hat, sich selbst neu zu zeichnen?
mP.
@mP Ja, das ist es sicherlich; Ich habe meine Frage bearbeitet, um dies knapp 5 Minuten vor Ihrem Kommentar klar zu machen :) Dies macht die Frage jedoch nicht ungültig. Unter der Annahme, dass das Verhalten in Chrome und FF zulässig ist - kein Fehler -, ist es in der Tat wichtig, auf Nummer sicher zu gehen.
Phrogz
@ Pekka Siehe meine 'Antwort' unten; Für IE9 muss Onload eingestellt sein, bevor src eingestellt wird, aber kein anderer Browser. (!) In der Tat überraschend.
Phrogz
@phrogz Hallo Phrogz, diese Funktionalität ist mir sehr wichtig; Im Android-Browser (die neueste Version) kann das Zuweisen von dataURL zu einer Bildquelle jedoch auch mit Ihrer "Playing It Safe" -Methode nicht ordnungsgemäß funktionieren. Könnten Sie mir weitere Vorschläge machen?
Alston

Antworten:

52

Da niemand hat noch keine Angaben darüber , wie gefunden hat dies angeblich verhalten, werden wir mit werden müssen zufrieden , wie es tut verhalten. Es folgen die Ergebnisse meiner Tests.

            | Ist Bilddaten verwendbar? Wird der Onload-Rückruf nach src gesetzt?
            | nach dem Einstellen von src? | wird geändert noch aufgerufen?
------------ + ----------------------------- + ------- ------------------------------
Safari 5 | ja | Ja                
------------ + ----------------------------- + ------- ------------------------------
Chrome 8 | ** nein ** (1. Seite laden), aber | Ja                
FireFox 3.6 | ja danach (zwischengespeichert) |                                     
------------ + ----------------------------- + ------- ------------------------------
IE8 | ja (32kB Datenlimit) | Ja         
------------ + ----------------------------- + ------- ------------------------------
IE9b | ja | **Nein**              
------------ + ----------------------------- + ------- ------------------------------

Zusammenfassend:

  • Sie können nicht davon ausgehen, dass die Bilddaten direkt nach dem Festlegen einer Daten-URL verfügbar sind. Sie müssen auf das onloadEreignis warten .
  • Aufgrund von IE9 können Sie den onloadHandler nach dem Festlegen von nicht festlegen srcund erwarten, dass er aufgerufen wird.
  • Die Empfehlungen in "Auf Nummer sicher gehen" (aus der obigen Frage) sind die einzige Möglichkeit, ein korrektes Verhalten sicherzustellen.

Wenn jemand Spezifikationen findet, die dies diskutieren, werde ich stattdessen gerne ihre Antwort akzeptieren.

Phrogz
quelle
3
Vielen Dank für die Recherche, die Sie hier durchgeführt haben. Ich habe mit der Möglichkeit zu kämpfen, dieses Verhalten für eine Offline-Webanwendung zu erzwingen, bei der ich mithilfe von Canvas Bericht erstatten muss, also habe ich ein bisschen mehr eingegraben. Die lebende WHATWG-Spezifikation nennt den zwischengespeicherten Fall tatsächlich als synchron und den nicht zwischengespeicherten Fall als asynchron. Schauen Sie sich Schritt 7 im Abschnitt src der Spezifikation an, der hier zu finden ist: whatwg.org/specs/web-apps/current-work/multipage/… . Es wird angezeigt, wenn sich das Bild in der "Liste der verfügbaren Bilder" des Dokuments befindet und synchron ausgefüllt wird, oder so habe ich es gelesen.
J Trana
@Phrogz In welchem ​​Stadium oder Dokumentstatus haben Sie diesen Test durchgeführt? Ich beobachte, dass Safari 5.1 das Chrome-Verhalten aufweist, aber das ist vor dem window.loadEreignis. Wurden Ihre Tests nach dem Laden oder vorher durchgeführt? Erinnerst du dich?
Hexalys
7

Nach der trueRückgabe der Kontrolle an die Rendering-Engine des Browsers werden die Testberichte in FF 4b9 und Chromium 8.0.552.237 erstellt. Ich habe diese Teile modifiziert:

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);

Update: Ja, ich verstehe, dass diese 'Antwort' eher wie ein Kommentar ist, wollte aber nur darauf hinweisen. Und nach dem Testen erhalte ich reproduzierbare Ergebnisse:

  • ohne setTimeout, in beiden Browsern bekomme ich falsedas erste Mal die Seite zu öffnen und trueerst nach dem Schlagen F5. Nachdem der Cache explizit geleert wurde, sagen beide falsenach dem erneuten Laden erneut.
  • mit setTimeoutdem Breiten- / Höhentest immer zu bewerten true.

In beiden Fällen wird das Bild erst beim erneuten Laden der Seite beim ersten Mal angezeigt.

Marcel Korpel
quelle
Wollen Sie mir sagen, dass Sie ohne diese Änderungen "Fehler" in FF und Chromium sehen? Meine Frage ist nicht, wie dies funktioniert - eine klare Einstellung onloadvor srcund nur das Verweisen auf das Bild im Rückruf funktioniert. Meine Frage ist, ob es Spezifikationen gibt, die beschreiben, wie dies funktionieren sollte.
Phrogz