From fdf2985adfc33e7c85f4e087f5741a7bedadebf0 Mon Sep 17 00:00:00 2001 From: Tetsuro Yoshikawa Date: Thu, 9 Feb 2017 22:27:55 +0900 Subject: [PATCH 1/5] Error in IE11 due to setting of type attribute If you assign an input type that is not supported by IE11 with an assignment expression, an error will occur. --- mithril.js | 5 +++++ mithril.min.js | 24 ++++++++++++------------ render/render.js | 5 +++++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/mithril.js b/mithril.js index c1ec91f0..cf18e1ac 100644 --- a/mithril.js +++ b/mithril.js @@ -773,6 +773,11 @@ var coreRenderer = function($window) { if (vnode.tag === "select" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return //setting option[value] to same value while having select open blinks select dropdown in Chrome if (vnode.tag === "option" && key2 === "value" && vnode.dom.value === value) return + // If you assign an input type1 that is not supported by IE 11 with an assignment expression, an error0 will occur. + if (vnode.tag === "input" && key2 === "type") { + element.setAttribute(key2, value); + return + } element[key2] = value } else { diff --git a/mithril.min.js b/mithril.min.js index 528305de..8e7cd361 100644 --- a/mithril.min.js +++ b/mithril.min.js @@ -23,20 +23,20 @@ v))}A--}else q--,A--;if(A Date: Fri, 10 Feb 2017 18:13:07 +1100 Subject: [PATCH 2/5] Avoid inaccurately inferring xhr abort. xhr.status can equal zero in non-abort scenarios, eg timeout or CORS failure. Instead use a variable to track aborts. --- request/request.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/request/request.js b/request/request.js index b153b0e9..c05ec0a3 100644 --- a/request/request.js +++ b/request/request.js @@ -53,7 +53,16 @@ module.exports = function($window, Promise) { if (useBody) args.data = args.serialize(args.data) else args.url = assemble(args.url, args.data) - var xhr = new $window.XMLHttpRequest() + var xhr = new $window.XMLHttpRequest(), + aborted = false, + _abort = xhr.abort + + + xhr.abort = function abort() { + aborted = true + _abort.call(xhr) + } + xhr.open(args.method, args.url, typeof args.async === "boolean" ? args.async : true, typeof args.user === "string" ? args.user : undefined, typeof args.password === "string" ? args.password : undefined) if (args.serialize === JSON.stringify && useBody) { @@ -71,9 +80,10 @@ module.exports = function($window, Promise) { if (typeof args.config === "function") xhr = args.config(xhr, args) || xhr xhr.onreadystatechange = function() { - // Don't throw errors on xhr.abort(). XMLHttpRequests ends up in a state of - // xhr.status == 0 and xhr.readyState == 4 if aborted after open, but before completion. - if (xhr.status && xhr.readyState === 4) { + // Don't throw errors on xhr.abort(). + if(aborted) return + + if (xhr.readyState === 4) { try { var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args)) if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { From 6a617aeb8737bb777d028f3af3f1c2a14fb1cc8c Mon Sep 17 00:00:00 2001 From: Bryce Gibson Date: Sun, 12 Feb 2017 16:38:45 +1100 Subject: [PATCH 3/5] Add tests for abort functionality. --- request/tests/test-request.js | 26 ++++++++++++++++++++++++++ test-utils/xhrMock.js | 16 ++++++++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/request/tests/test-request.js b/request/tests/test-request.js index ade0465c..d903a4ed 100644 --- a/request/tests/test-request.js +++ b/request/tests/test-request.js @@ -390,6 +390,22 @@ o.spec("xhr", function() { o(xhr.getRequestHeader("Accept")).equals("application/json, text/*") } }) + o("doesn't fail on abort", function(done) { + var s = new Date + mock.$defineRoutes({ + "GET /item": function() { + return {status: 200, responseText: JSON.stringify({a: 1})} + } + }) + var failed = false + xhr({method: "GET", url: "/item", config: function (xhr) { setTimeout(function() { xhr.abort() }, 0) }}).catch(function() { + failed = true + }).then(function() { + o(failed).equals(false) + }).then(function() { + done() + }) + }) /*o("data maintains after interpolate", function() { mock.$defineRoutes({ "PUT /items/:x": function() { @@ -463,5 +479,15 @@ o.spec("xhr", function() { }) }) }) + o("rejects on cors-like error", function(done) { + mock.$defineRoutes({ + "GET /item": function(request) { + return {status: 0} + } + }) + xhr({method: "GET", url: "/item"}).catch(function(e) { + o(e instanceof Error).equals(true) + }).then(done) + }) }) }) diff --git a/test-utils/xhrMock.js b/test-utils/xhrMock.js index 37d48e92..57c2887f 100644 --- a/test-utils/xhrMock.js +++ b/test-utils/xhrMock.js @@ -15,6 +15,7 @@ module.exports = function() { XMLHttpRequest: function XMLHttpRequest() { var args = {} var headers = {} + var aborted = false this.setRequestHeader = function(header, value) { headers[header] = value } @@ -32,11 +33,15 @@ module.exports = function() { } this.send = function(body) { var self = this - var handler = routes[args.method + " " + args.pathname] || serverErrorHandler.bind(null, args.pathname) - var data = handler({url: args.pathname, query: args.search || {}, body: body || null}) + if(!aborted) { + var handler = routes[args.method + " " + args.pathname] || serverErrorHandler.bind(null, args.pathname) + var data = handler({url: args.pathname, query: args.search || {}, body: body || null}) + self.status = data.status + self.responseText = data.responseText + } else { + self.status = 0 + } self.readyState = 4 - self.status = data.status - self.responseText = data.responseText if (args.async === true) { var s = new Date callAsync(function() { @@ -44,6 +49,9 @@ module.exports = function() { }) } } + this.abort = function() { + aborted = true + } }, document: { createElement: function(tag) { From 27881af6685e37fcd1347cb94d258d63d535a49a Mon Sep 17 00:00:00 2001 From: Bryce Gibson Date: Wed, 15 Feb 2017 08:34:40 +1100 Subject: [PATCH 4/5] Ignore xhr.status for "file://" requests. --- request/request.js | 4 +++- request/tests/test-request.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/request/request.js b/request/request.js index c05ec0a3..552fa515 100644 --- a/request/request.js +++ b/request/request.js @@ -2,6 +2,8 @@ var buildQueryString = require("../querystring/build") +var FILE_PROTOCOL_REGEX = new RegExp('^file://', 'i') + module.exports = function($window, Promise) { var callbackCount = 0 @@ -86,7 +88,7 @@ module.exports = function($window, Promise) { if (xhr.readyState === 4) { try { var response = (args.extract !== extract) ? args.extract(xhr, args) : args.deserialize(args.extract(xhr, args)) - if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || FILE_PROTOCOL_REGEX.test(args.url)) { resolve(cast(args.type, response)) } else { diff --git a/request/tests/test-request.js b/request/tests/test-request.js index d903a4ed..43e8c065 100644 --- a/request/tests/test-request.js +++ b/request/tests/test-request.js @@ -406,6 +406,23 @@ o.spec("xhr", function() { done() }) }) + o("doesn't fail on file:// status 0", function(done) { + var s = new Date + mock.$defineRoutes({ + "GET /item": function() { + return {status: 0, responseText: JSON.stringify({a: 1})} + } + }) + var failed = false + xhr({method: "GET", url: "file:///item"}).catch(function() { + failed = true + }).then(function(data) { + o(failed).equals(false) + o(data).deepEquals({a: 1}) + }).then(function() { + done() + }) + }) /*o("data maintains after interpolate", function() { mock.$defineRoutes({ "PUT /items/:x": function() { From a946ef531597d5147c790eea58f335713e2187d1 Mon Sep 17 00:00:00 2001 From: Bryce Gibson Date: Wed, 15 Feb 2017 13:30:50 +1100 Subject: [PATCH 5/5] Fix xhr abort test. Need to be more creative about ensuring that onreadystatechange is called, but that the test conditions execute after that the function (that does nothing...) is called. --- request/tests/test-request.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/request/tests/test-request.js b/request/tests/test-request.js index 43e8c065..273d592c 100644 --- a/request/tests/test-request.js +++ b/request/tests/test-request.js @@ -397,13 +397,30 @@ o.spec("xhr", function() { return {status: 200, responseText: JSON.stringify({a: 1})} } }) + var failed = false - xhr({method: "GET", url: "/item", config: function (xhr) { setTimeout(function() { xhr.abort() }, 0) }}).catch(function() { + var resolved = false + function handleAbort(xhr) { + var onreadystatechange = xhr.onreadystatechange // probably not set yet + var testonreadystatechange = function() { + onreadystatechange.call(xhr) + setTimeout(function() { // allow promises to (not) resolve first + o(failed).equals(false) + o(resolved).equals(false) + done() + }, 0) + } + Object.defineProperty(xhr, 'onreadystatechange', { + set: function(val) { onreadystatechange = val } + , get: function() { return testonreadystatechange } + }) + xhr.abort() + } + xhr({method: "GET", url: "/item", config: handleAbort}).catch(function() { failed = true - }).then(function() { - o(failed).equals(false) - }).then(function() { - done() + }) + .then(function() { + resolved = true }) }) o("doesn't fail on file:// status 0", function(done) {