diff --git a/dlls/jscript/global.c b/dlls/jscript/global.c index 8f6080dc1b1..501da79e71b 100644 --- a/dlls/jscript/global.c +++ b/dlls/jscript/global.c @@ -930,11 +930,127 @@ static HRESULT JSGlobal_encodeURIComponent(script_ctx_t *ctx, vdisp_t *jsthis, W return S_OK; } +/* ECMA-262 3rd Edition 15.1.3.2 */ static HRESULT JSGlobal_decodeURIComponent(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) { - FIXME("\n"); - return E_NOTIMPL; + BSTR str, ret; + const WCHAR *ptr; + WCHAR *out_ptr; + DWORD len = 0; + HRESULT hres; + + TRACE("\n"); + + if(!arg_cnt(dp)) { + if(retv) { + ret = SysAllocString(undefinedW); + if(!ret) + return E_OUTOFMEMORY; + + V_VT(retv) = VT_BSTR; + V_BSTR(retv) = ret; + } + + return S_OK; + } + + hres = to_string(ctx, get_arg(dp, 0), ei, &str); + if(FAILED(hres)) + return hres; + + ptr = str; + while(*ptr) { + if(*ptr == '%') { + char octets[4]; + unsigned char mask = 0x80; + int i, size, num_bytes = 0; + if(hex_to_int(*(ptr+1)) < 0 || hex_to_int(*(ptr+2)) < 0) { + FIXME("Throw URIError: Invalid hex sequence\n"); + SysFreeString(str); + return E_FAIL; + } + octets[0] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2)); + ptr += 3; + while(octets[0] & mask) { + mask = mask >> 1; + ++num_bytes; + } + if(num_bytes == 1 || num_bytes > 4) { + FIXME("Throw URIError: Invalid initial UTF character\n"); + SysFreeString(str); + return E_FAIL; + } + for(i = 1; i < num_bytes; ++i) { + if(*ptr != '%'){ + FIXME("Throw URIError: Incomplete UTF sequence\n"); + SysFreeString(str); + return E_FAIL; + } + if(hex_to_int(*(ptr+1)) < 0 || hex_to_int(*(ptr+2)) < 0) { + FIXME("Throw URIError: Invalid hex sequence\n"); + SysFreeString(str); + return E_FAIL; + } + octets[i] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2)); + ptr += 3; + } + size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, octets, + num_bytes ? num_bytes : 1, NULL, 0); + if(size == 0) { + FIXME("Throw URIError: Invalid UTF sequence\n"); + SysFreeString(str); + return E_FAIL; + } + len += size; + }else { + ++ptr; + ++len; + } + } + + out_ptr = ret = SysAllocStringLen(NULL, len); + if(!ret) { + SysFreeString(str); + return E_OUTOFMEMORY; + } + + ptr = str; + while(*ptr) { + if(*ptr == '%') { + char octets[4]; + unsigned char mask = 0x80; + int i, size, num_bytes = 0; + octets[0] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2)); + ptr += 3; + while(octets[0] & mask) { + mask = mask >> 1; + ++num_bytes; + } + for(i = 1; i < num_bytes; ++i) { + octets[i] = (hex_to_int(*(ptr+1)) << 4) + hex_to_int(*(ptr+2)); + ptr += 3; + } + size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, octets, + num_bytes ? num_bytes : 1, out_ptr, len); + len -= size; + out_ptr += size; + }else { + *out_ptr++ = *ptr++; + --len; + } + } + + SysFreeString(str); + + if(retv) { + V_VT(retv) = VT_BSTR; + V_BSTR(retv) = ret; + }else { + SysFreeString(ret); + } + + return S_OK; } static const builtin_prop_t JSGlobal_props[] = { diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index 1fb2f6d8f98..42a79e8f71e 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -60,24 +60,44 @@ ok(tmp === "abc", "encodeURI('abc') = " + tmp); tmp = encodeURIComponent("abc"); ok(tmp === "abc", "encodeURIComponent('abc') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "abc", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("{abc}"); ok(tmp === "%7Babc%7D", "encodeURIComponent('{abc}') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "{abc}", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent(""); ok(tmp === "", "encodeURIComponent('') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("\01\02\03\04"); ok(tmp === "%01%02%03%04", "encodeURIComponent('\\01\\02\\03\\04') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "\01\02\03\04", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("{#@}"); ok(tmp === "%7B%23%40%7D", "encodeURIComponent('{#@}') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "{#@}", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("\xa1 "); ok(tmp === "%C2%A1%20", "encodeURIComponent(\\xa1 ) = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "\xa1 ", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("\xffff"); ok(tmp.length === 8, "encodeURIComponent('\\xffff').length = " + tmp.length); +dec = decodeURIComponent(tmp); +ok(dec === "\xffff", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent("abcABC123;/?:@&=+$,-_.!~*'()"); ok(tmp === "abcABC123%3B%2F%3F%3A%40%26%3D%2B%24%2C-_.!~*'()", "encodeURIComponent('abcABC123;/?:@&=+$,-_.!~*'()') = " + tmp); +dec = decodeURIComponent(tmp); +ok(dec === "abcABC123;/?:@&=+$,-_.!~*'()", "decodeURIComponent('" + tmp + "') = " + dec); tmp = encodeURIComponent(); ok(tmp === "undefined", "encodeURIComponent() = " + tmp); tmp = encodeURIComponent("abc", "test"); ok(tmp === "abc", "encodeURIComponent('abc') = " + tmp); +dec = decodeURIComponent(); +ok(dec === "undefined", "decodeURIComponent() = " + dec); +dec = decodeURIComponent("abc", "test"); +ok(dec === "abc", "decodeURIComponent('abc') = " + dec); tmp = escape("abc"); ok(tmp === "abc", "escape('abc') = " + tmp);