Closed Bug 1160892 Opened 9 years ago Closed 9 years ago

Url.createObjectURL(blob) creates invalid URL on unicode non-ascii domain

Categories

(Core :: DOM: Core & HTML, defect)

34 Branch
defect
Not set
major

Tracking

()

RESOLVED FIXED
mozilla40
Tracking Status
firefox40 --- fixed

People

(Reporter: ab, Assigned: baku)

References

()

Details

(Keywords: dev-doc-needed, regression, site-compat)

Attachments

(1 file, 2 obsolete files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/600.4.10 (KHTML, like Gecko) Version/6.2.4 Safari/537.85.13

Steps to reproduce:

Chat-webapp https://xn----0gab.net uses web socket binary file transfers -> decryption->blob->Url.createObjectURL(blob)->preview/download  
This worked well until Firefox 34 and works well with Tor 



Actual results:

with Firefox 35++ Url.createObjectURL(blob) does not provide reliable urls for object/iFrame/img embedding or download anymore.  


Expected results:

Try with Tor.
https://xn----0gab.net works well with all browsers (beside MS IE) including Tor and Tails (iceweasel) since march 2014. The situation with firefox failing to work with the messenger decreased user numbers dramatically.
Severity: normal → major
OS: Unspecified → All
Hardware: Unspecified → All
(In reply to ABri from comment #0)
> User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5)
> AppleWebKit/600.4.10 (KHTML, like Gecko) Version/6.2.4 Safari/537.85.13
> 
> Steps to reproduce:
> 
> Chat-webapp https://xn----0gab.net uses web socket binary file transfers ->
> decryption->blob->Url.createObjectURL(blob)->preview/download  
> This worked well until Firefox 34 and works well with Tor 

Could you explain steps to reproduce the issue when the user has loaded this page, please. Where do I need to click?
Flags: needinfo?(ab)
Hi

1. open 
https://xn----0gab.net/chat
dismiss the alert for firefox users.

2. type one character and „return“ at the nickname

3. copy the selected link for the chat partner into a new tab or window and open it - this will become the chat partner window.

.. both windows will connect.

4. now on one of the clients click "choose file“ and choose one i.e. a pdf file with less than 5MB length
(click on „Datei“ if you send an image file - the secret image function works seamless)

5. the file transfer will be notified in the other client window. Try click on „vorschau“ (= „preview“) or click on the link to download.

In previous versions of firefox previewing within the window and download was fast and easy - now nothing happens. 
Please have a look at the blob-adress.
In firefox <35  the address was „blob:hgjhg….“ 
firefox >34 shows the link as „blob:https://xn——0gab.net/hgjh…..“ but seems to be unable to follow and unravel it.

Thanks for looking into this !!
Flags: needinfo?(ab)
It's a website issue, because not follwing the spec change in Bug 1058470.

You need to include the website origin in the blob URI.
Blocks: 1058470
Component: Untriaged → Desktop
Product: Core → Tech Evangelism
Version: 35 Branch → Firefox 34
I do not think it's a website issue, because i do not change the URL delivered from window.URL.createObjectURL(blob) within the code but copy it directly into the website (see the non-minified code below). That should work and it does work with all other browsers.

If i should guess, i would say it might be PUNICode issue, because https://xn----0gab.net is punicode for "ö-ö.net" and the "Umlaute" might be encoded wrongly in the createObjectURL(blob) function. I had this issue with openSSL handling the site'S SSL-Certificate. 

This is the relevant code part:

....

    } else {
        var reader = new FileReader();
        reader.addEventListener("loadend", function () {
            var i = 1,
                j = 0,
                a = (new Uint8Array(reader.result)),
                l = a.length,
                mac = new BLAKE2s(32),
                macHash,
                bab = new ArrayBuffer(l - 171),
                // b = []
                b = new Uint8Array(bab),
                encBuf = s20seedBuf(a[0]),
                S20 = Salsa20.init(encBuf[0], encBuf[1]),
                name = "",
                type = "";
            for (; i < 121; i++) {
                name += String.fromCharCode(a[i] ^ S20.getBytes(1)[0]);
            }
            name = name.replace(/ /g, "");
            for (; i < 171; i++) {
                type += String.fromCharCode(a[i] ^ S20.getBytes(1)[0]);
            }
            type = type.replace(/ /g, "");
            if (type.match(/^(text|secret)/)) {
                var hlp = new ArrayBuffer(l - 171),
                    hlp8 = new Uint8Array(hlp),
                    hlp16 = new Uint16Array(hlp);
                for (; i < l; hlp8[j++] = a[i++] ^ S20.getBytes(1)[0]) {}
                mac.update( hlp8.subarray(0,(hlp8.length-32)) );
                macHash = hlp8.subarray(hlp8.length-32);
                b = LZBA.decompress(hlp16.subarray(0,hlp16.length-16), true);
                
            } else {
                for (; i < l; b[j++] = a[i++] ^ S20.getBytes(1)[0]) {}
                macHash = b.subarray(b.length-32);
                b = new Uint8Array( b.subarray(0,(b.length-32)) );
                mac.update( b );
                
            };
            // check mac          
            if (!mac.equal( macHash )) {
            	websocket.send('!!!DISCONNECT!');
                websocket.close();
                alert("MAC failed; Uebertragung fehlerhaft - Connection down.");
            };
            //
            var imgShown = false;
            if (type === "canvas/png") {
                
                (....)
                
            } else if (type === "secret/png") {
                showMessage("Bild hochgeladen. (secretImage)", false, true);
                
                ( ....)
            
---> Bug-RELEVANT Code starts here
            
            } else {
                // Ausgabe als Link oder Object
                var blob = new Blob([b.buffer], {
                    type: type
                });
                url = window.URL.createObjectURL(blob);
                delay(window.URL.revokeObjectURL, 300000, url); // 5 min
                link = document.createElement("a");
                link.textContent = name + "_(" + parseInt(b.length / 1000) + " kb)";
                link.innerHTML += "</br>";
                if (showMessage("Datei hochgeladen. (" + type + ")", false, true)) {
                    var msgs = output.getElementsByTagName("p");
                    msgs[msgs.length - 1].insertBefore(link, msgs[msgs.length - 1].childNodes[1]);
                };
                // navigator.info[0]!=="Chrome" && 
                if (navigator.info[0] !== "Chrome" && ((type.search(/html/i) === -1 && type.search(/appl/i) === -1) || type.search(/pdf/i) !== -1)) {
                    showUpload(msgs, url, type);
                }
                // safari
                if (navigator.info[0].search(/safari/i) != -1 && navigator.mobile && !navigator.userAgent.match(/ipad|tablet|android/i)) {} else {
                    link.setAttribute("download", name);
                    link.href = url;
                    link.target = "_blanc";
                    link.addEventListener("click", function () {
                        delay(window.URL.revokeObjectURL, 30000, url); // 30 sec
                        var ta = this.previousElementSibling || this.previousElement;
                        if (ta.type === "submit") {
                            this.parentNode.removeChild(ta)
                        }
                        ta = this.previousElementSibling || this.previousElement;
                        if (ta.type === "textarea") {
                            ta.value = "Datei im Download-Ordner.";
                        }
                        this.parentNode.removeChild(this);
                    }, false);
                }
            }
        });
        reader.readAsArrayBuffer(e.data);
    }
}

showUpload = function (msgs, url, type) {
    var buttn = document.createElement("button");
    buttn.typ = type;
    buttn.url = url;
    buttn.className = "vorschau";
    buttn.innerHTML = "Vorschau";
    buttn.onclick = function () {
        type = this.typ;
        url = this.url;
        try {
            if (type.search(/image/i) !== -1) {
                var obj = document.createElement("center");
                var cImg = document.createElement("img");
                cImg.setAttribute("src", encodeURI(url));
                cImg.setAttribute("type", type);
                cImg.style.maxWidth = "360px";
                cImg.style.maxHeight = "360px";
                obj.appendChild(cImg);
            } else if (type.search(/pdf/i) !== -1) {
                var obj = document.createElement("iframe");
                obj.setAttribute("src", encodeURI(url));
                // obj.setAttribute("sandbox", "");
                obj.setAttribute("type", type);
                obj.setAttribute("width", "360");
                obj.setAttribute("height", "360");
            } else if (type.search(/audio/i) !== -1) {
                var obj = document.createElement("audio");
                obj.setAttribute("src", encodeURI(url));
                obj.setAttribute("controls", "");
                // obj.setAttribute("sandbox", "");
                obj.setAttribute("type", type);
                obj.setAttribute("width", "360");
                obj.setAttribute("height", "360");
            } else {
                var obj = document.createElement("object");
                obj.setAttribute("data", encodeURI(url));
                obj.setAttribute("sandbox", "");
                obj.setAttribute("type", type);
                obj.setAttribute("width", "360");
                obj.setAttribute("max-height", "360");
            }
            output.appendChild(obj);
        } catch (e) {}
        try {
            this.parentNode.removeChild(this);
        } catch (e) {}
    }
    msgs[msgs.length - 1].insertBefore(buttn, msgs[msgs.length - 1].childNodes[1]);
};
CC'ing Andrea about the PUNICode in the blob URL.
Flags: needinfo?(amarchesini)
Abri, if you run this small demo to display images (or PDF) via blob on your server, does it wok?
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications#Example.3A_Using_object_URLs_to_display_images
Flags: needinfo?(ab)
Hey Loic,
it's broken on firefox but works with the other.
You may try out by entering "punicodeTest" as nickname.

1. open https://xn----0gab.net/c
2. dismiss the firefox alert
3. type "punicodeTest" + return at the nickname input
Flags: needinfo?(ab)
Assignee: nobody → amarchesini
Flags: needinfo?(amarchesini)
Attached patch url.patch (obsolete) — Splinter Review
I would like to write a test but I don't know how to have a page loaded from a unicode non-ascii URL with our mochitest framework.
Attachment #8601963 - Flags: review?(bugs)
Component: Desktop → DOM
Keywords: regression
Product: Tech Evangelism → Core
Summary: Url.createObjectURL(blob) creates invalid URL → Url.createObjectURL(blob) creates invalid URL on unicode non-ascii domain
Version: Firefox 34 → 34 Branch
Attached patch url.patch (obsolete) — Splinter Review
Same patch with tests.
Attachment #8601963 - Attachment is obsolete: true
Attachment #8601963 - Flags: review?(bugs)
Attachment #8602061 - Flags: review?(bugs)
Why does this need a browser test?  You should be able to window.open() the non-ascii thing from a regular mochitest...

Also, it's worth documenting why this should use the ASCII origin, not the UTF one, because it's not at all obvious to me why it matters which one is used here.
Status: UNCONFIRMED → NEW
Ever confirmed: true
Attached patch url.patchSplinter Review
mochitest instead a browser test.
Attachment #8602061 - Attachment is obsolete: true
Attachment #8602061 - Flags: review?(bugs)
Attachment #8602191 - Flags: review?(bugs)
(In reply to Andrea Marchesini (:baku) from comment #9)
> Created attachment 8601963 [details] [diff] [review]
> url.patch
> 
> I would like to write a test but I don't know how to have a page loaded from
> a unicode non-ascii URL with our mochitest framework.

Hi Andrea,
i'am puzzled.
I would like to understand, if you were able to reproduce my problem, and if i had been right in understanding it as a problem with the punicode address? And of course, if you see a short term solution coming up?
Hi ABri,

Yes! I can reproduce the problem using your website. Thanks a lot for the test account.
The problem is that we do something wrong with punicode address. Here the problem:

The blob URI is composed by 'blob:' + the origin of the domain + '/' + UUID

In the current ff code, we convert the unicode version of the origin address to ASCII but without following the puny-code encoding.

I wrote a patch and I'm sure it's going to be reviewed soon.
But more interesting, it seem that we also have a bug in the spec: Usually the URI are ASCII, but from the File API spec point of view it, we have to use the unicode version of the origin. But doing this, a normal URI parser, will escape the origin using % values ignoring the puny-code address.

Annevk filed a bug for the spec as you can see from comment 12.

However, my patch works because it generates the blob URI using the ASCII origin. In your case a blob URI will be: blob:https://xn----0gab.net/<UUID>
Attachment #8602191 - Flags: review?(bugs) → review+
https://hg.mozilla.org/mozilla-central/rev/ddab177fd4c4
Status: NEW → RESOLVED
Closed: 9 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla40
It's still broken in the latest Nightly which has the patch.

Blob URL given by the website:
blob:https://%C3%83%C2%B6-%C3%83%C2%B6.net/<UUID>
which is:
blob:https://xn----ddab1pc.net/<UUID>
instead of:
blob:https://xn----0gab.net/<UUID>
The last nightly doesn't have this patch. The patch is landed to m-c just today: Comment 17.
Can you test it with the nightly of tomorrow? Thanks!
It's fixed on my side (both testcases) with the latest Nightly on Win 7.
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.