From 93e9cec6c6a93f7a9473381c6daf215172fc6b7c Mon Sep 17 00:00:00 2001
From: Jmeas <jellyes2@gmail.com>
Date: Thu, 7 Dec 2017 01:18:19 -0800
Subject: [PATCH 01/24] Support abort API

---
 fetch.js | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/fetch.js b/fetch.js
index f2f466d7..4e02c2db 100644
--- a/fetch.js
+++ b/fetch.js
@@ -1,7 +1,21 @@
 (function(self) {
   'use strict';
 
-  if (self.fetch) {
+  function canAbortFetch() {
+    if (!self.AbortController || !self.AbortSignal) {
+      return false
+    }
+  
+    var abortController = new self.AbortController()
+  
+    var request = new self.Request('http://a', {
+      signal: abortController.signal
+    })
+  
+    return Boolean(request.signal)
+  }
+
+  if (self.fetch && canAbortFetch()) {
     return
   }
 
@@ -443,6 +457,10 @@
         reject(new TypeError('Network request failed'))
       }
 
+      xhr.onabort = function() {
+        reject(new DOMException('Aborted', 'AbortError'))
+      }
+
       xhr.open(request.method, request.url, true)
 
       if (request.credentials === 'include') {
@@ -459,6 +477,10 @@
         xhr.setRequestHeader(name, value)
       })
 
+      if (init.signal && init.signal.addEventListener) {
+        init.signal.addEventListener('abort', xhr.abort)
+      }
+
       xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
     })
   }

From 53565c1aec2a5ca026a58e0ce3460c654fc55a98 Mon Sep 17 00:00:00 2001
From: Jmeas <jellyes2@gmail.com>
Date: Thu, 7 Dec 2017 08:49:11 -0800
Subject: [PATCH 02/24] Support abort without replacing native fetch

---
 fetch.js | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/fetch.js b/fetch.js
index 4e02c2db..41267405 100644
--- a/fetch.js
+++ b/fetch.js
@@ -1,21 +1,7 @@
 (function(self) {
   'use strict';
 
-  function canAbortFetch() {
-    if (!self.AbortController || !self.AbortSignal) {
-      return false
-    }
-  
-    var abortController = new self.AbortController()
-  
-    var request = new self.Request('http://a', {
-      signal: abortController.signal
-    })
-  
-    return Boolean(request.signal)
-  }
-
-  if (self.fetch && canAbortFetch()) {
+  if (self.fetch) {
     return
   }
 

From 9a7d232905f786b704651409e74b3d665adc2dac Mon Sep 17 00:00:00 2001
From: James Smith <jamesplease2@gmail.com>
Date: Thu, 7 Dec 2017 12:08:35 -0800
Subject: [PATCH 03/24] Remove event listener; use request.signal

---
 fetch.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fetch.js b/fetch.js
index 41267405..5b42a839 100644
--- a/fetch.js
+++ b/fetch.js
@@ -385,6 +385,7 @@
     this.statusText = 'statusText' in options ? options.statusText : 'OK'
     this.headers = new Headers(options.headers)
     this.url = options.url || ''
+    this.signal = options.signal
     this._initBody(bodyInit)
   }
 
@@ -445,6 +446,7 @@
 
       xhr.onabort = function() {
         reject(new DOMException('Aborted', 'AbortError'))
+        request.signal.removeEventListener('abort', xhr.abort)
       }
 
       xhr.open(request.method, request.url, true)
@@ -463,8 +465,8 @@
         xhr.setRequestHeader(name, value)
       })
 
-      if (init.signal && init.signal.addEventListener) {
-        init.signal.addEventListener('abort', xhr.abort)
+      if (request.signal && request.signal.addEventListener) {
+        request.signal.addEventListener('abort', xhr.abort)
       }
 
       xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)

From fd5c261bd12c1a5fb6fd6a24e9dd1697d5a82fd8 Mon Sep 17 00:00:00 2001
From: James Smith <jamesplease2@gmail.com>
Date: Thu, 7 Dec 2017 12:11:08 -0800
Subject: [PATCH 04/24] Skip creation of XHR if signal is already aborted

---
 fetch.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fetch.js b/fetch.js
index 5b42a839..fc6d1b4b 100644
--- a/fetch.js
+++ b/fetch.js
@@ -425,6 +425,10 @@
       var request = new Request(input, init)
       var xhr = new XMLHttpRequest()
 
+      if (request.signal && request.signal.aborted) {
+        return reject(new DOMException('Aborted', 'AbortError'));
+      }
+
       xhr.onload = function() {
         var options = {
           status: xhr.status,

From 318e95f0609e5d5dc2d1f8d57263c04f154ee950 Mon Sep 17 00:00:00 2001
From: Jmeas <jellyes2@gmail.com>
Date: Thu, 7 Dec 2017 21:42:26 -0800
Subject: [PATCH 05/24] First pass at documentation

---
 README.md | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/README.md b/README.md
index 32cc3c20..dd0ed1e2 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ expected to uphold this code.
     * [Sending cookies](#sending-cookies)
     * [Receiving cookies](#receiving-cookies)
     * [Obtaining the Response URL](#obtaining-the-response-url)
+    * [Aborting requests](#aborting-requests)
 * [Browser Support](#browser-support)
 
 ## Read this first
@@ -260,6 +261,47 @@ response.headers['X-Request-URL'] = request.url
 This server workaround is necessary if you need reliable `response.url` in
 Firefox < 32, Chrome < 37, Safari, or IE.
 
+#### Aborting requests
+
+This polyfill supports
+[the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch).
+However, aborting a fetch requires use of two additional DOM APIs:
+[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
+and
+[AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
+Typically, browsers that do not support fetch will also not support
+AbortController or AbortSignal.
+
+Consequently, you will need to find a polyfill for these APIs to abort fetches.
+
+Once you have an AbortController and AbortSignal polyfill in place, you can
+abort a fetch like so:
+
+```js
+const controller = new AbortController()
+
+fetch('/avatars', {
+  method: 'POST',
+  body: data,
+  signal: controller.signal
+})
+  .then(
+    function(response) {
+      console.log('request succeeded', response)
+    },
+    function(ex) {
+      if (ex.name === 'AbortError') {
+        console.log('request aborted')
+      } else {
+        console.log('request failed, but it was not aborted')
+      }
+    }
+  )
+
+// some time later...
+controller.abort();
+```
+
 ## Browser Support
 
 - Chrome

From 0e1b651f4492e31fb3b18297c574807fdb3aafa4 Mon Sep 17 00:00:00 2001
From: James Smith <jamesplease2@gmail.com>
Date: Fri, 8 Dec 2017 09:18:56 -0800
Subject: [PATCH 06/24] More tweaks based on feedback

---
 README.md | 23 ++++++-----------------
 fetch.js  | 10 +++++++---
 2 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index dd0ed1e2..a5958315 100644
--- a/README.md
+++ b/README.md
@@ -270,9 +270,8 @@ However, aborting a fetch requires use of two additional DOM APIs:
 and
 [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
 Typically, browsers that do not support fetch will also not support
-AbortController or AbortSignal.
-
-Consequently, you will need to find a polyfill for these APIs to abort fetches.
+AbortController or AbortSignal. Consequently, you will need to include an
+additional polyfill for these APIs to abort fetches.
 
 Once you have an AbortController and AbortSignal polyfill in place, you can
 abort a fetch like so:
@@ -281,22 +280,12 @@ abort a fetch like so:
 const controller = new AbortController()
 
 fetch('/avatars', {
-  method: 'POST',
-  body: data,
   signal: controller.signal
+}).catch(function(ex) {
+  if (ex.name === 'AbortError') {
+    console.log('request aborted')
+  }
 })
-  .then(
-    function(response) {
-      console.log('request succeeded', response)
-    },
-    function(ex) {
-      if (ex.name === 'AbortError') {
-        console.log('request aborted')
-      } else {
-        console.log('request failed, but it was not aborted')
-      }
-    }
-  )
 
 // some time later...
 controller.abort();
diff --git a/fetch.js b/fetch.js
index fc6d1b4b..a1d80373 100644
--- a/fetch.js
+++ b/fetch.js
@@ -429,6 +429,10 @@
         return reject(new DOMException('Aborted', 'AbortError'));
       }
 
+      function abortXhr() {
+        xhr.abort();
+      }
+
       xhr.onload = function() {
         var options = {
           status: xhr.status,
@@ -450,7 +454,7 @@
 
       xhr.onabort = function() {
         reject(new DOMException('Aborted', 'AbortError'))
-        request.signal.removeEventListener('abort', xhr.abort)
+        request.signal.removeEventListener('abort', abortXhr)
       }
 
       xhr.open(request.method, request.url, true)
@@ -469,8 +473,8 @@
         xhr.setRequestHeader(name, value)
       })
 
-      if (request.signal && request.signal.addEventListener) {
-        request.signal.addEventListener('abort', xhr.abort)
+      if (request.signal) {
+        request.signal.addEventListener('abort', abortXhr)
       }
 
       xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)

From d47f6c9a7f92ee6248545a9e91e834a8443bafa6 Mon Sep 17 00:00:00 2001
From: James Smith <jamesplease2@gmail.com>
Date: Fri, 8 Dec 2017 09:34:05 -0800
Subject: [PATCH 07/24] Remove some semicolons

---
 fetch.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fetch.js b/fetch.js
index a1d80373..1d4009a0 100644
--- a/fetch.js
+++ b/fetch.js
@@ -426,11 +426,11 @@
       var xhr = new XMLHttpRequest()
 
       if (request.signal && request.signal.aborted) {
-        return reject(new DOMException('Aborted', 'AbortError'));
+        return reject(new DOMException('Aborted', 'AbortError'))
       }
 
       function abortXhr() {
-        xhr.abort();
+        xhr.abort()
       }
 
       xhr.onload = function() {

From 529c9fa0d1dcd88d8582aaf4a6a3b6b12d246d14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 13 Dec 2017 15:13:08 +0100
Subject: [PATCH 08/24] Add `signal` property to Request instead of Response

---
 fetch.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fetch.js b/fetch.js
index 1d4009a0..205f063c 100644
--- a/fetch.js
+++ b/fetch.js
@@ -317,6 +317,7 @@
       }
       this.method = input.method
       this.mode = input.mode
+      this.signal = input.signal
       if (!body && input._bodyInit != null) {
         body = input._bodyInit
         input.bodyUsed = true
@@ -331,6 +332,7 @@
     }
     this.method = normalizeMethod(options.method || this.method || 'GET')
     this.mode = options.mode || this.mode || null
+    this.signal = options.signal || this.signal
     this.referrer = null
 
     if ((this.method === 'GET' || this.method === 'HEAD') && body) {
@@ -385,7 +387,6 @@
     this.statusText = 'statusText' in options ? options.statusText : 'OK'
     this.headers = new Headers(options.headers)
     this.url = options.url || ''
-    this.signal = options.signal
     this._initBody(bodyInit)
   }
 

From 8ba0cdb9b3286d4e47ba239bd56e4ef7c05ae0d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 13 Dec 2017 15:13:30 +0100
Subject: [PATCH 09/24] Polyfill DOMException if missing

---
 fetch.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/fetch.js b/fetch.js
index 205f063c..6ea03311 100644
--- a/fetch.js
+++ b/fetch.js
@@ -417,6 +417,12 @@
     return new Response(null, {status: status, headers: {location: url}})
   }
 
+  var DOMException = self.DOMException || function(message, name) {
+    var error = new Error(message)
+    error.name = name
+    return error
+  }
+
   self.Headers = Headers
   self.Request = Request
   self.Response = Response

From 074c89e2fc117f692b11c8b6b4fda55a330b600b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 13 Dec 2017 15:27:13 +0100
Subject: [PATCH 10/24] Polyfill DOMException

In PhantomJS, DOMException exists but is not a constructor.
---
 fetch.js | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/fetch.js b/fetch.js
index 6ea03311..555814a0 100644
--- a/fetch.js
+++ b/fetch.js
@@ -417,10 +417,18 @@
     return new Response(null, {status: status, headers: {location: url}})
   }
 
-  var DOMException = self.DOMException || function(message, name) {
-    var error = new Error(message)
-    error.name = name
-    return error
+  var DOMException = self.DOMException
+  try {
+    new DOMException()
+  } catch(err) {
+    DOMException = function(message, name) {
+      this.message = message
+      this.name = name
+      var error = Error(message)
+      this.stack = error.stack
+    }
+    DOMException.prototype = Object.create(Error.prototype)
+    DOMException.prototype.constructor = DOMException
   }
 
   self.Headers = Headers

From b8322292e3147d3f9b9d30ccc6acebd8fbb51445 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 13 Dec 2017 15:27:49 +0100
Subject: [PATCH 11/24] Add tests for aborting requests

---
 script/server |  6 ++++++
 test/test.js  | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/script/server b/script/server
index 00993f84..753090f7 100755
--- a/script/server
+++ b/script/server
@@ -80,6 +80,12 @@ var routes = {
     res.writeHead(204);
     res.end();
   },
+  '/slow': function(res) {
+    setTimeout(function() {
+      res.writeHead(200);
+      res.end();
+    }, 100);
+  },
   '/error': function(res) {
     res.destroy();
   },
diff --git a/test/test.js b/test/test.js
index 0db2307f..f2fd7c50 100644
--- a/test/test.js
+++ b/test/test.js
@@ -952,6 +952,49 @@ suite('fetch method', function() {
     })
   })
 
+  suite('aborting', function() {
+    test('initially aborted signal', function() {
+      return fetch('/request', {
+        signal: {aborted: true}
+      }).then(function() {
+        assert.ok(false)
+      }, function(error) {
+        assert.equal(error.name, 'AbortError')
+      })
+    })
+
+    test('mid-request', function() {
+      var signal = {
+        aborted: false,
+        addEventListener: function(name, fn) {
+          assert.equal(name, 'abort')
+          this._handler = fn
+        },
+        removeEventListener: function(name, fn) {
+          assert.equal(name, 'abort')
+          if (this._handler === fn) {
+            delete this._handler
+          }
+        }
+      }
+
+      setTimeout(function() {
+        if (signal._handler) {
+          signal._handler()
+        }
+      }, 30)
+
+      return fetch('/slow', {
+        signal: signal
+      }).then(function() {
+        assert.ok(false)
+      }, function(error) {
+        assert.equal(error.name, 'AbortError')
+        assert.isUndefined(signal._handler)
+      })
+    })
+  })
+
   suite('response', function() {
     test('populates body', function() {
       return fetch('/hello').then(function(response) {

From c6aff302f6fd42be9f058481ffdaf92e6a8d4a41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 13 Dec 2017 15:33:20 +0100
Subject: [PATCH 12/24] Avoid creating XMLHttpRequest if we're immediately
 aborting

---
 fetch.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fetch.js b/fetch.js
index 555814a0..cc760d0d 100644
--- a/fetch.js
+++ b/fetch.js
@@ -438,12 +438,13 @@
   self.fetch = function(input, init) {
     return new Promise(function(resolve, reject) {
       var request = new Request(input, init)
-      var xhr = new XMLHttpRequest()
 
       if (request.signal && request.signal.aborted) {
         return reject(new DOMException('Aborted', 'AbortError'))
       }
 
+      var xhr = new XMLHttpRequest()
+
       function abortXhr() {
         xhr.abort()
       }

From d9aa590ecae34a5c3190615252a614a5faa565a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Fri, 2 Feb 2018 02:00:10 +0000
Subject: [PATCH 13/24] Clear event listener for all xhr completion status.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Vieira <joaoguerravieira@gmail.com>
---
 fetch.js     |  8 +++++++-
 test/test.js | 28 ++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/fetch.js b/fetch.js
index cc760d0d..072b897c 100644
--- a/fetch.js
+++ b/fetch.js
@@ -470,7 +470,6 @@
 
       xhr.onabort = function() {
         reject(new DOMException('Aborted', 'AbortError'))
-        request.signal.removeEventListener('abort', abortXhr)
       }
 
       xhr.open(request.method, request.url, true)
@@ -491,6 +490,13 @@
 
       if (request.signal) {
         request.signal.addEventListener('abort', abortXhr)
+
+        xhr.onreadystatechange = function() {
+          // DONE (success or failure)
+          if (xhr.readyState === 4) {
+            request.signal.removeEventListener('abort', abortXhr)
+          }
+        }
       }
 
       xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
diff --git a/test/test.js b/test/test.js
index f2fd7c50..f2b2bcb8 100644
--- a/test/test.js
+++ b/test/test.js
@@ -993,6 +993,34 @@ suite('fetch method', function() {
         assert.isUndefined(signal._handler)
       })
     })
+
+    test('does not leak memory', function() {
+      var signal = {
+        aborted: false,
+        addEventListener: function(name, fn) {
+          this._handler = fn
+        },
+        removeEventListener: function(name, fn) {
+          if (this._handler === fn) {
+            delete this._handler
+          }
+        }
+      }
+
+      // success
+      return fetch('/request', {
+        signal: signal
+      }).then(function() {
+        assert.isUndefined(signal._handler)
+      }).then(function () {
+        // failure
+        return fetch('/boom', {
+          signal: signal
+        }).catch(function() {
+          assert.isUndefined(signal._handler)
+        })
+      });
+    })
   })
 
   suite('response', function() {

From 9f9a00fd8aba5a919cdff09c086ff71daeb39c38 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Fri, 2 Feb 2018 17:04:57 +0000
Subject: [PATCH 14/24] Test signal reuse. Add AbortSignal polyfill.

---
 .jshintrc                    |  3 +-
 test/abortsignal-polyfill.js | 68 +++++++++++++++++++++++++++++++
 test/test.html               |  1 +
 test/test.js                 | 78 +++++++++++++++++++++---------------
 test/worker.js               |  1 +
 5 files changed, 118 insertions(+), 33 deletions(-)
 create mode 100644 test/abortsignal-polyfill.js

diff --git a/.jshintrc b/.jshintrc
index c451b546..d179472e 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -20,6 +20,7 @@
   "worker": true,
   "globals": {
     "JSON": false,
-    "URLSearchParams": false
+    "URLSearchParams": false,
+    "AbortSignal": false
   }
 }
diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
new file mode 100644
index 00000000..49d347da
--- /dev/null
+++ b/test/abortsignal-polyfill.js
@@ -0,0 +1,68 @@
+// Simple AbortSignal polyfill used for testing.
+(function(self) {
+  'use strict';
+
+  if (self.AbortSignal) {
+    return
+  }
+
+  // Polyfill EventTarget if not available.
+  self.EventTarget = self.EventTarget || (function () {
+    function EventTarget () {
+      this._listeners = {}
+    }
+    EventTarget.prototype.addEventListener = function(name, fn) {
+      if (!(name in this._listeners)) {
+        this._listeners[name] = []
+      }
+      this._listeners[name].push(fn)
+    }
+    EventTarget.prototype.removeEventListener = function(name, fn) {
+      if (!(name in this._listeners)) {
+        return
+      }
+      var stack = this._listeners[name]
+      for (var i = 0; i < stack.length; i++) {
+        if (stack[i] === fn){
+          stack.splice(i, 1)
+          return
+        }
+      }
+    }
+    EventTarget.prototype.dispatchEvent = function(event) {
+      if (!(event.type in this._listeners)) {
+        return
+      }
+      function debounce (fn) {
+        setTimeout(function () {
+          fn.call(this, event)
+        }, 0)
+      }
+      var stack = this._listeners[event.type]
+      for (var i = 0; i < stack.length; i++) {
+        debounce(stack[i])
+      }
+    }
+
+    return EventTarget
+  })();
+
+  function AbortSignal () {
+    self.EventTarget.call(this)
+    this.aborted = false
+  }
+  AbortSignal.prototype = Object.create(self.EventTarget.prototype)
+  AbortSignal.prototype.constructor = AbortSignal
+
+  AbortSignal.prototype.dispatchEvent = function(event) {
+    if (event.type === 'abort'){
+      this.aborted = true
+      if (typeof this.onabort === 'function') {
+        this.onabort.call(this, event)
+      }
+    }
+    self.EventTarget.prototype.dispatchEvent.call(this, event)
+  }
+
+  self.AbortSignal = AbortSignal
+})(typeof self !== 'undefined' ? self : this);
\ No newline at end of file
diff --git a/test/test.html b/test/test.html
index 8a1e48f1..bbf24760 100644
--- a/test/test.html
+++ b/test/test.html
@@ -33,6 +33,7 @@
   </script>
 
   <script src="/node_modules/promise-polyfill/promise.js"></script>
+  <script src="/test/abortsignal-polyfill.js"></script>
   <script src="/test/test.js"></script>
   <script src="/fetch.js"></script>
 
diff --git a/test/test.js b/test/test.js
index f2b2bcb8..30653fdd 100644
--- a/test/test.js
+++ b/test/test.js
@@ -954,8 +954,11 @@ suite('fetch method', function() {
 
   suite('aborting', function() {
     test('initially aborted signal', function() {
+      const signal = new AbortSignal()
+      signal.aborted = true
+
       return fetch('/request', {
-        signal: {aborted: true}
+        signal: signal
       }).then(function() {
         assert.ok(false)
       }, function(error) {
@@ -964,24 +967,10 @@ suite('fetch method', function() {
     })
 
     test('mid-request', function() {
-      var signal = {
-        aborted: false,
-        addEventListener: function(name, fn) {
-          assert.equal(name, 'abort')
-          this._handler = fn
-        },
-        removeEventListener: function(name, fn) {
-          assert.equal(name, 'abort')
-          if (this._handler === fn) {
-            delete this._handler
-          }
-        }
-      }
+      const signal = new AbortSignal()
 
       setTimeout(function() {
-        if (signal._handler) {
-          signal._handler()
-        }
+        signal.dispatchEvent({ type: 'abort' })
       }, 30)
 
       return fetch('/slow', {
@@ -990,36 +979,61 @@ suite('fetch method', function() {
         assert.ok(false)
       }, function(error) {
         assert.equal(error.name, 'AbortError')
-        assert.isUndefined(signal._handler)
       })
     })
 
+    test('abort multiple with same signal', function() {
+      const signal = new AbortSignal()
+
+      setTimeout(function() {
+        signal.dispatchEvent({ type: 'abort' })
+      }, 30)
+
+      return Promise.all([
+        fetch('/slow', {
+          signal: signal
+        }).then(function() {
+          assert.ok(false)
+        }, function(error) {
+          assert.equal(error.name, 'AbortError')
+        }),
+        fetch('/slow', {
+          signal: signal
+        }).then(function() {
+          assert.ok(false)
+        }, function(error) {
+          assert.equal(error.name, 'AbortError')
+        })
+      ])
+    })
+
     test('does not leak memory', function() {
-      var signal = {
-        aborted: false,
-        addEventListener: function(name, fn) {
-          this._handler = fn
-        },
-        removeEventListener: function(name, fn) {
-          if (this._handler === fn) {
-            delete this._handler
-          }
-        }
-      }
+      const signal = new AbortSignal()
 
       // success
       return fetch('/request', {
         signal: signal
       }).then(function() {
-        assert.isUndefined(signal._handler)
+        assert.deepEqual(signal._listeners['abort'], [])
       }).then(function () {
         // failure
         return fetch('/boom', {
           signal: signal
         }).catch(function() {
-          assert.isUndefined(signal._handler)
+          assert.deepEqual(signal._listeners['abort'], [])
         })
-      });
+      }).then(function () {
+        // aborted
+        setTimeout(function() {
+          signal.dispatchEvent({ type: 'abort' })
+        }, 30)
+
+        return fetch('/slow', {
+          signal: signal
+        }).catch(function() {
+          assert.deepEqual(signal._listeners['abort'], [])
+        })
+      })
     })
   })
 
diff --git a/test/worker.js b/test/worker.js
index 025d0dd1..6feb0a33 100644
--- a/test/worker.js
+++ b/test/worker.js
@@ -5,6 +5,7 @@ mocha.setup('tdd')
 self.assert = chai.assert
 
 importScripts('/node_modules/promise-polyfill/promise.js')
+importScripts('/test/abortsignal-polyfill.js')
 importScripts('/test/test.js')
 importScripts('/fetch.js')
 

From bab0f3deaae80afe8e0d8fbf0dcc4aa597636adc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Fri, 2 Feb 2018 18:12:45 +0000
Subject: [PATCH 15/24] Consistent implementation of EventTarget as in MDN.

---
 test/abortsignal-polyfill.js | 28 +++++++++++++++-------------
 test/test.js                 |  6 +++---
 2 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
index 49d347da..8e892be4 100644
--- a/test/abortsignal-polyfill.js
+++ b/test/abortsignal-polyfill.js
@@ -9,28 +9,29 @@
   // Polyfill EventTarget if not available.
   self.EventTarget = self.EventTarget || (function () {
     function EventTarget () {
-      this._listeners = {}
+      this.listeners = {}
     }
-    EventTarget.prototype.addEventListener = function(name, fn) {
-      if (!(name in this._listeners)) {
-        this._listeners[name] = []
+    EventTarget.prototype.listeners = null;
+    EventTarget.prototype.addEventListener = function(type, callback) {
+      if (!(type in this.listeners)) {
+        this.listeners[type] = []
       }
-      this._listeners[name].push(fn)
+      this.listeners[type].push(callback)
     }
-    EventTarget.prototype.removeEventListener = function(name, fn) {
-      if (!(name in this._listeners)) {
+    EventTarget.prototype.removeEventListener = function(type, callback) {
+      if (!(type in this.listeners)) {
         return
       }
-      var stack = this._listeners[name]
-      for (var i = 0; i < stack.length; i++) {
-        if (stack[i] === fn){
+      var stack = this.listeners[type]
+      for (var i = 0, l = stack.length; i < l; i++) {
+        if (stack[i] === callback){
           stack.splice(i, 1)
           return
         }
       }
     }
     EventTarget.prototype.dispatchEvent = function(event) {
-      if (!(event.type in this._listeners)) {
+      if (!(event.type in this.listeners)) {
         return
       }
       function debounce (fn) {
@@ -38,10 +39,11 @@
           fn.call(this, event)
         }, 0)
       }
-      var stack = this._listeners[event.type]
-      for (var i = 0; i < stack.length; i++) {
+      var stack = this.listeners[event.type]
+      for (var i = 0, l = stack.length; i < l; i++) {
         debounce(stack[i])
       }
+      return !event.defaultPrevented
     }
 
     return EventTarget
diff --git a/test/test.js b/test/test.js
index 30653fdd..534ed600 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1014,13 +1014,13 @@ suite('fetch method', function() {
       return fetch('/request', {
         signal: signal
       }).then(function() {
-        assert.deepEqual(signal._listeners['abort'], [])
+        assert.deepEqual(signal.listeners['abort'], [])
       }).then(function () {
         // failure
         return fetch('/boom', {
           signal: signal
         }).catch(function() {
-          assert.deepEqual(signal._listeners['abort'], [])
+          assert.deepEqual(signal.listeners['abort'], [])
         })
       }).then(function () {
         // aborted
@@ -1031,7 +1031,7 @@ suite('fetch method', function() {
         return fetch('/slow', {
           signal: signal
         }).catch(function() {
-          assert.deepEqual(signal._listeners['abort'], [])
+          assert.deepEqual(signal.listeners['abort'], [])
         })
       })
     })

From 0bed2bad5923280809c12e82662491fd85560a0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sat, 3 Feb 2018 12:03:09 +0000
Subject: [PATCH 16/24] Split EventTarget and AbortSignal polyfills.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Vieira <joaoguerravieira@gmail.com>
---
 .jshintrc                    |  1 +
 test/abortsignal-polyfill.js | 86 +++++++++++++++++++-----------------
 2 files changed, 47 insertions(+), 40 deletions(-)

diff --git a/.jshintrc b/.jshintrc
index d179472e..3c25f7fc 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -21,6 +21,7 @@
   "globals": {
     "JSON": false,
     "URLSearchParams": false,
+    "EventTarget": false,
     "AbortSignal": false
   }
 }
diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
index 8e892be4..80f5da02 100644
--- a/test/abortsignal-polyfill.js
+++ b/test/abortsignal-polyfill.js
@@ -1,59 +1,65 @@
-// Simple AbortSignal polyfill used for testing.
+// EventTarget Polyfill
 (function(self) {
   'use strict';
 
-  if (self.AbortSignal) {
+  if (self.EventTarget) {
     return
   }
 
-  // Polyfill EventTarget if not available.
-  self.EventTarget = self.EventTarget || (function () {
-    function EventTarget () {
-      this.listeners = {}
+  function EventTarget () {
+    this.listeners = {}
+  }
+  EventTarget.prototype.listeners = null;
+  EventTarget.prototype.addEventListener = function(type, callback) {
+    if (!(type in this.listeners)) {
+      this.listeners[type] = []
     }
-    EventTarget.prototype.listeners = null;
-    EventTarget.prototype.addEventListener = function(type, callback) {
-      if (!(type in this.listeners)) {
-        this.listeners[type] = []
-      }
-      this.listeners[type].push(callback)
+    this.listeners[type].push(callback)
+  }
+  EventTarget.prototype.removeEventListener = function(type, callback) {
+    if (!(type in this.listeners)) {
+      return
     }
-    EventTarget.prototype.removeEventListener = function(type, callback) {
-      if (!(type in this.listeners)) {
+    var stack = this.listeners[type]
+    for (var i = 0, l = stack.length; i < l; i++) {
+      if (stack[i] === callback){
+        stack.splice(i, 1)
         return
       }
-      var stack = this.listeners[type]
-      for (var i = 0, l = stack.length; i < l; i++) {
-        if (stack[i] === callback){
-          stack.splice(i, 1)
-          return
-        }
-      }
     }
-    EventTarget.prototype.dispatchEvent = function(event) {
-      if (!(event.type in this.listeners)) {
-        return
-      }
-      function debounce (fn) {
-        setTimeout(function () {
-          fn.call(this, event)
-        }, 0)
-      }
-      var stack = this.listeners[event.type]
-      for (var i = 0, l = stack.length; i < l; i++) {
-        debounce(stack[i])
-      }
-      return !event.defaultPrevented
+  }
+  EventTarget.prototype.dispatchEvent = function(event) {
+    if (!(event.type in this.listeners)) {
+      return
     }
+    function debounce (fn) {
+      setTimeout(function () {
+        fn.call(this, event)
+      }, 0)
+    }
+    var stack = this.listeners[event.type]
+    for (var i = 0, l = stack.length; i < l; i++) {
+      debounce(stack[i])
+    }
+    return !event.defaultPrevented
+  }
 
-    return EventTarget
-  })();
+  self.EventTarget = EventTarget
+})(typeof self !== 'undefined' ? self : this);
+  
+// AbortSignal polyfill
+(function(self) {
+  'use strict';
+
+  if (self.AbortSignal) {
+    return
+  }
 
   function AbortSignal () {
-    self.EventTarget.call(this)
+    EventTarget.call(this)
     this.aborted = false
   }
-  AbortSignal.prototype = Object.create(self.EventTarget.prototype)
+  AbortSignal.prototype = Object.create(EventTarget.prototype)
   AbortSignal.prototype.constructor = AbortSignal
 
   AbortSignal.prototype.dispatchEvent = function(event) {
@@ -63,7 +69,7 @@
         this.onabort.call(this, event)
       }
     }
-    self.EventTarget.prototype.dispatchEvent.call(this, event)
+    EventTarget.prototype.dispatchEvent.call(this, event)
   }
 
   self.AbortSignal = AbortSignal

From 738d51a057478803dbeea41e0d369e3914ab3d7d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sat, 3 Feb 2018 12:26:07 +0000
Subject: [PATCH 17/24] Add tests with signal within Request object.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Vieira <joaoguerravieira@gmail.com>
---
 test/test.js | 36 ++++++++++++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 4 deletions(-)

diff --git a/test/test.js b/test/test.js
index 534ed600..bb717d2b 100644
--- a/test/test.js
+++ b/test/test.js
@@ -954,7 +954,7 @@ suite('fetch method', function() {
 
   suite('aborting', function() {
     test('initially aborted signal', function() {
-      const signal = new AbortSignal()
+      var signal = new AbortSignal()
       signal.aborted = true
 
       return fetch('/request', {
@@ -966,8 +966,21 @@ suite('fetch method', function() {
       })
     })
 
+    test('initially aborted signal within Request', function () {
+      var signal = new AbortSignal()
+      signal.aborted = true
+
+      var request = new Request('/request', { signal: signal })
+
+      return fetch(request).then(function() {
+        assert.ok(false)
+      }, function(error) {
+        assert.equal(error.name, 'AbortError')
+      })
+    })
+
     test('mid-request', function() {
-      const signal = new AbortSignal()
+      var signal = new AbortSignal()
 
       setTimeout(function() {
         signal.dispatchEvent({ type: 'abort' })
@@ -982,8 +995,23 @@ suite('fetch method', function() {
       })
     })
 
+    test('mid-request within Request', function() {
+      var signal = new AbortSignal()
+      var request = new Request('/slow', { signal: signal })
+
+      setTimeout(function() {
+        signal.dispatchEvent({ type: 'abort' })
+      }, 30)
+
+      return fetch(request).then(function() {
+        assert.ok(false)
+      }, function(error) {
+        assert.equal(error.name, 'AbortError')
+      })
+    })
+
     test('abort multiple with same signal', function() {
-      const signal = new AbortSignal()
+      var signal = new AbortSignal()
 
       setTimeout(function() {
         signal.dispatchEvent({ type: 'abort' })
@@ -1008,7 +1036,7 @@ suite('fetch method', function() {
     })
 
     test('does not leak memory', function() {
-      const signal = new AbortSignal()
+      var signal = new AbortSignal()
 
       // success
       return fetch('/request', {

From 8f778f394316ea947264853dc61ba9df2be2ee6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sat, 3 Feb 2018 17:54:29 +0000
Subject: [PATCH 18/24] Fix EventTarget callback invocation context.

---
 test/abortsignal-polyfill.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
index 80f5da02..061e3e02 100644
--- a/test/abortsignal-polyfill.js
+++ b/test/abortsignal-polyfill.js
@@ -32,9 +32,10 @@
     if (!(event.type in this.listeners)) {
       return
     }
+    var self = this
     function debounce (fn) {
       setTimeout(function () {
-        fn.call(this, event)
+        fn.call(self, event)
       }, 0)
     }
     var stack = this.listeners[event.type]

From f0e24f8ebb2d6f8e57c7ea69abfc3fac1ff4b36b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sat, 3 Feb 2018 19:04:19 +0000
Subject: [PATCH 19/24] Native EventTarget is not inheritable.

---
 .jshintrc                    |  1 -
 test/abortsignal-polyfill.js | 31 ++++++++++---------------------
 2 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/.jshintrc b/.jshintrc
index 3c25f7fc..d179472e 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -21,7 +21,6 @@
   "globals": {
     "JSON": false,
     "URLSearchParams": false,
-    "EventTarget": false,
     "AbortSignal": false
   }
 }
diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
index 061e3e02..1aed0ad4 100644
--- a/test/abortsignal-polyfill.js
+++ b/test/abortsignal-polyfill.js
@@ -1,22 +1,22 @@
-// EventTarget Polyfill
+// AbortSignal polyfill
 (function(self) {
   'use strict';
 
-  if (self.EventTarget) {
+  if (self.AbortSignal) {
     return
   }
 
-  function EventTarget () {
+  function Emitter () {
     this.listeners = {}
   }
-  EventTarget.prototype.listeners = null;
-  EventTarget.prototype.addEventListener = function(type, callback) {
+  Emitter.prototype.listeners = null;
+  Emitter.prototype.addEventListener = function(type, callback) {
     if (!(type in this.listeners)) {
       this.listeners[type] = []
     }
     this.listeners[type].push(callback)
   }
-  EventTarget.prototype.removeEventListener = function(type, callback) {
+  Emitter.prototype.removeEventListener = function(type, callback) {
     if (!(type in this.listeners)) {
       return
     }
@@ -28,7 +28,7 @@
       }
     }
   }
-  EventTarget.prototype.dispatchEvent = function(event) {
+  Emitter.prototype.dispatchEvent = function(event) {
     if (!(event.type in this.listeners)) {
       return
     }
@@ -45,22 +45,11 @@
     return !event.defaultPrevented
   }
 
-  self.EventTarget = EventTarget
-})(typeof self !== 'undefined' ? self : this);
-  
-// AbortSignal polyfill
-(function(self) {
-  'use strict';
-
-  if (self.AbortSignal) {
-    return
-  }
-
   function AbortSignal () {
-    EventTarget.call(this)
+    Emitter.call(this)
     this.aborted = false
   }
-  AbortSignal.prototype = Object.create(EventTarget.prototype)
+  AbortSignal.prototype = Object.create(Emitter.prototype)
   AbortSignal.prototype.constructor = AbortSignal
 
   AbortSignal.prototype.dispatchEvent = function(event) {
@@ -70,7 +59,7 @@
         this.onabort.call(this, event)
       }
     }
-    EventTarget.prototype.dispatchEvent.call(this, event)
+    Emitter.prototype.dispatchEvent.call(this, event)
   }
 
   self.AbortSignal = AbortSignal

From 36bf6925ca05792277b3ee4cc75aea3f14042882 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sun, 4 Feb 2018 15:06:07 +0000
Subject: [PATCH 20/24] Replace AbortSignal polyfill with
 abortcontroller-polyfill module.

---
 .jshintrc                    |  2 +-
 package.json                 |  1 +
 test/abortsignal-polyfill.js | 66 ------------------------------------
 test/test.html               |  2 +-
 test/test.js                 | 35 +++++++++----------
 test/worker.js               |  2 +-
 6 files changed, 22 insertions(+), 86 deletions(-)
 delete mode 100644 test/abortsignal-polyfill.js

diff --git a/.jshintrc b/.jshintrc
index d179472e..8041a1ae 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -21,6 +21,6 @@
   "globals": {
     "JSON": false,
     "URLSearchParams": false,
-    "AbortSignal": false
+    "AbortController": false
   }
 }
diff --git a/package.json b/package.json
index e6d80cc9..2f1c1e18 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
   "repository": "github/fetch",
   "license": "MIT",
   "devDependencies": {
+    "abortcontroller-polyfill": "1.1.1",
     "chai": "1.10.0",
     "jshint": "2.8.0",
     "mocha": "2.1.0",
diff --git a/test/abortsignal-polyfill.js b/test/abortsignal-polyfill.js
deleted file mode 100644
index 1aed0ad4..00000000
--- a/test/abortsignal-polyfill.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// AbortSignal polyfill
-(function(self) {
-  'use strict';
-
-  if (self.AbortSignal) {
-    return
-  }
-
-  function Emitter () {
-    this.listeners = {}
-  }
-  Emitter.prototype.listeners = null;
-  Emitter.prototype.addEventListener = function(type, callback) {
-    if (!(type in this.listeners)) {
-      this.listeners[type] = []
-    }
-    this.listeners[type].push(callback)
-  }
-  Emitter.prototype.removeEventListener = function(type, callback) {
-    if (!(type in this.listeners)) {
-      return
-    }
-    var stack = this.listeners[type]
-    for (var i = 0, l = stack.length; i < l; i++) {
-      if (stack[i] === callback){
-        stack.splice(i, 1)
-        return
-      }
-    }
-  }
-  Emitter.prototype.dispatchEvent = function(event) {
-    if (!(event.type in this.listeners)) {
-      return
-    }
-    var self = this
-    function debounce (fn) {
-      setTimeout(function () {
-        fn.call(self, event)
-      }, 0)
-    }
-    var stack = this.listeners[event.type]
-    for (var i = 0, l = stack.length; i < l; i++) {
-      debounce(stack[i])
-    }
-    return !event.defaultPrevented
-  }
-
-  function AbortSignal () {
-    Emitter.call(this)
-    this.aborted = false
-  }
-  AbortSignal.prototype = Object.create(Emitter.prototype)
-  AbortSignal.prototype.constructor = AbortSignal
-
-  AbortSignal.prototype.dispatchEvent = function(event) {
-    if (event.type === 'abort'){
-      this.aborted = true
-      if (typeof this.onabort === 'function') {
-        this.onabort.call(this, event)
-      }
-    }
-    Emitter.prototype.dispatchEvent.call(this, event)
-  }
-
-  self.AbortSignal = AbortSignal
-})(typeof self !== 'undefined' ? self : this);
\ No newline at end of file
diff --git a/test/test.html b/test/test.html
index bbf24760..9d4d82fb 100644
--- a/test/test.html
+++ b/test/test.html
@@ -33,7 +33,7 @@
   </script>
 
   <script src="/node_modules/promise-polyfill/promise.js"></script>
-  <script src="/test/abortsignal-polyfill.js"></script>
+  <script src="/node_modules/abortcontroller-polyfill/dist/abortcontroller.js"></script>
   <script src="/test/test.js"></script>
   <script src="/fetch.js"></script>
 
diff --git a/test/test.js b/test/test.js
index bb717d2b..4a102753 100644
--- a/test/test.js
+++ b/test/test.js
@@ -954,11 +954,11 @@ suite('fetch method', function() {
 
   suite('aborting', function() {
     test('initially aborted signal', function() {
-      var signal = new AbortSignal()
-      signal.aborted = true
+      var controller = new AbortController()
+      controller.abort()
 
       return fetch('/request', {
-        signal: signal
+        signal: controller.signal
       }).then(function() {
         assert.ok(false)
       }, function(error) {
@@ -967,10 +967,10 @@ suite('fetch method', function() {
     })
 
     test('initially aborted signal within Request', function () {
-      var signal = new AbortSignal()
-      signal.aborted = true
+      var controller = new AbortController()
+      controller.abort()
 
-      var request = new Request('/request', { signal: signal })
+      var request = new Request('/request', { signal: controller.signal })
 
       return fetch(request).then(function() {
         assert.ok(false)
@@ -980,14 +980,14 @@ suite('fetch method', function() {
     })
 
     test('mid-request', function() {
-      var signal = new AbortSignal()
+      var controller = new AbortController()
 
       setTimeout(function() {
-        signal.dispatchEvent({ type: 'abort' })
+        controller.abort()
       }, 30)
 
       return fetch('/slow', {
-        signal: signal
+        signal: controller.signal
       }).then(function() {
         assert.ok(false)
       }, function(error) {
@@ -996,11 +996,11 @@ suite('fetch method', function() {
     })
 
     test('mid-request within Request', function() {
-      var signal = new AbortSignal()
-      var request = new Request('/slow', { signal: signal })
+      var controller = new AbortController()
+      var request = new Request('/slow', { signal: controller.signal })
 
       setTimeout(function() {
-        signal.dispatchEvent({ type: 'abort' })
+        controller.abort()
       }, 30)
 
       return fetch(request).then(function() {
@@ -1011,22 +1011,22 @@ suite('fetch method', function() {
     })
 
     test('abort multiple with same signal', function() {
-      var signal = new AbortSignal()
+      var controller = new AbortController()
 
       setTimeout(function() {
-        signal.dispatchEvent({ type: 'abort' })
+        controller.abort()
       }, 30)
 
       return Promise.all([
         fetch('/slow', {
-          signal: signal
+          signal: controller.signal
         }).then(function() {
           assert.ok(false)
         }, function(error) {
           assert.equal(error.name, 'AbortError')
         }),
         fetch('/slow', {
-          signal: signal
+          signal: controller.signal
         }).then(function() {
           assert.ok(false)
         }, function(error) {
@@ -1036,7 +1036,8 @@ suite('fetch method', function() {
     })
 
     test('does not leak memory', function() {
-      var signal = new AbortSignal()
+      var controller = new AbortController()
+      var signal = controller.signal
 
       // success
       return fetch('/request', {
diff --git a/test/worker.js b/test/worker.js
index 6feb0a33..f18c938a 100644
--- a/test/worker.js
+++ b/test/worker.js
@@ -5,7 +5,7 @@ mocha.setup('tdd')
 self.assert = chai.assert
 
 importScripts('/node_modules/promise-polyfill/promise.js')
-importScripts('/test/abortsignal-polyfill.js')
+importScripts('/node_modules/abortcontroller-polyfill/dist/abortcontroller.js')
 importScripts('/test/test.js')
 importScripts('/fetch.js')
 

From 7f6b8d2eb3e7d64cc3e8ec2c93332939ab5d859c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joa=CC=83o=20Vieira?= <joaoguerravieira@gmail.com>
Date: Sun, 4 Feb 2018 18:41:17 +0000
Subject: [PATCH 21/24] Upgrade abortcontroller-polyfill.

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2f1c1e18..9684bff4 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
   "repository": "github/fetch",
   "license": "MIT",
   "devDependencies": {
-    "abortcontroller-polyfill": "1.1.1",
+    "abortcontroller-polyfill": "1.1.2",
     "chai": "1.10.0",
     "jshint": "2.8.0",
     "mocha": "2.1.0",

From f03586047c100c5e5171afc2f71a74492309c8e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 23 May 2018 15:56:17 +0200
Subject: [PATCH 22/24] Scope abortcontroller to polyfill only

---
 package.json   | 2 +-
 test/test.html | 2 +-
 test/worker.js | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package.json b/package.json
index 8416e3dd..5153a7f4 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
   "repository": "github/fetch",
   "license": "MIT",
   "devDependencies": {
-    "abortcontroller-polyfill": "1.1.2",
+    "abortcontroller-polyfill": "^1.1.9",
     "chai": "1.10.0",
     "eslint": "^4.19.1",
     "eslint-plugin-github": "^1.0.0",
diff --git a/test/test.html b/test/test.html
index 1d4f0c59..eea0df32 100644
--- a/test/test.html
+++ b/test/test.html
@@ -33,7 +33,7 @@
   </script>
 
   <script src="/node_modules/promise-polyfill/promise.js"></script>
-  <script src="/node_modules/abortcontroller-polyfill/dist/abortcontroller.js"></script>
+  <script src="/node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js"></script>
   <script src="/dist/fetch.umd.js"></script>
   <script src="/test/test.js"></script>
 
diff --git a/test/worker.js b/test/worker.js
index 5cd1a36e..5bc87c41 100644
--- a/test/worker.js
+++ b/test/worker.js
@@ -5,7 +5,7 @@ mocha.setup('tdd')
 self.assert = chai.assert
 
 importScripts('/node_modules/promise-polyfill/promise.js')
-importScripts('/node_modules/abortcontroller-polyfill/dist/abortcontroller.js')
+importScripts('/node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js')
 importScripts('/dist/fetch.umd.js')
 importScripts('/test/test.js')
 

From 2bd7f22032272dc5d0ab507da69b84060bf6804f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 23 May 2018 16:00:34 +0200
Subject: [PATCH 23/24] Export DOMException for `instanceof` checks

---
 fetch.js     | 2 +-
 test/test.js | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/fetch.js b/fetch.js
index cd4c660b..b684ff1b 100644
--- a/fetch.js
+++ b/fetch.js
@@ -424,7 +424,7 @@ Response.redirect = function(url, status) {
   return new Response(null, {status: status, headers: {location: url}})
 }
 
-var DOMException = self.DOMException
+export var DOMException = self.DOMException
 try {
   new DOMException()
 } catch (err) {
diff --git a/test/test.js b/test/test.js
index 6bbb2f81..1308c63c 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1060,6 +1060,7 @@ exercise.forEach(function(exerciseMode) {
               assert.ok(false)
             },
             function(error) {
+              assert.instanceOf(error, WHATWGFetch.DOMException)
               assert.equal(error.name, 'AbortError')
             }
           )

From c873843dc8cbdb4d5af652a99476e90515133ffd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mislav=20Marohni=C4=87?= <mislav.marohnic@gmail.com>
Date: Wed, 23 May 2018 16:15:48 +0200
Subject: [PATCH 24/24] Polish documentation for aborting requests

---
 README.md | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index a5958315..ff3e5e7f 100644
--- a/README.md
+++ b/README.md
@@ -266,17 +266,17 @@ Firefox < 32, Chrome < 37, Safari, or IE.
 This polyfill supports
 [the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch).
 However, aborting a fetch requires use of two additional DOM APIs:
-[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
-and
+[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and
 [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
 Typically, browsers that do not support fetch will also not support
-AbortController or AbortSignal. Consequently, you will need to include an
-additional polyfill for these APIs to abort fetches.
-
-Once you have an AbortController and AbortSignal polyfill in place, you can
-abort a fetch like so:
+AbortController or AbortSignal. Consequently, you will need to include
+[an additional polyfill](https://github.com/mo/abortcontroller-polyfill#readme)
+for these APIs to abort fetches:
 
 ```js
+import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
+import {fetch} from 'whatwg-fetch'
+
 const controller = new AbortController()
 
 fetch('/avatars', {
@@ -288,7 +288,7 @@ fetch('/avatars', {
 })
 
 // some time later...
-controller.abort();
+controller.abort()
 ```
 
 ## Browser Support