diff --git a/core/core.services.yml b/core/core.services.yml
index 24fa98eaaaaa..b9aa505b0f36 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -765,6 +765,7 @@ services:
arguments: ['@element_info']
tags:
- { name: render.main_content_renderer, format: drupal_ajax }
+ - { name: render.main_content_renderer, format: iframeupload }
main_content_renderer.dialog:
class: Drupal\Core\Render\MainContent\DialogRenderer
arguments: ['@title_resolver']
diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponse.php b/core/lib/Drupal/Core/Ajax/AjaxResponse.php
index 54f8d72b4e26..1bc3d7e0a97a 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponse.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponse.php
@@ -115,6 +115,42 @@ class AjaxResponse extends JsonResponse {
if ($this->data == '{}') {
$this->setData($this->ajaxRender($request));
}
+
+ // IE 9 does not support XHR 2 (http://caniuse.com/#feat=xhr2), so
+ // for that browser, jquery.form submits requests containing a file upload
+ // via an IFRAME rather than via XHR. Since the response is being sent to
+ // an IFRAME, it must be formatted as HTML. Specifically:
+ // - It must use the text/html content type or else the browser will
+ // present a download prompt. Note: This applies to both file uploads
+ // as well as any ajax request in a form with a file upload form.
+ // - It must place the JSON data into a textarea to prevent browser
+ // extensions such as Linkification and Skype's Browser Highlighter
+ // from applying HTML transformations such as URL or phone number to
+ // link conversions on the data values.
+ //
+ // Since this affects the format of the output, it could be argued that
+ // this should be implemented as a separate Accept MIME type. However,
+ // that would require separate variants for each type of AJAX request
+ // (e.g., drupal-ajax, drupal-dialog, drupal-modal), so for expediency,
+ // this browser workaround is implemented via a GET or POST parameter.
+ //
+ // @see http://malsup.com/jquery/form/#file-upload
+ // @see https://drupal.org/node/1009382
+ // @see https://drupal.org/node/2339491
+ // @see Drupal.ajax.prototype.beforeSend()
+ $accept = $request->headers->get('accept');
+
+ if (strpos($accept, 'text/html') !== FALSE) {
+ $this->headers->set('Content-Type', 'text/html; charset=utf-8');
+
+ // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
+ // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
+ // links. This corrupts the JSON response. Protect the integrity of the
+ // JSON data by making it the value of a textarea.
+ // @see http://malsup.com/jquery/form/#file-upload
+ // @see http://drupal.org/node/1009382
+ $this->setContent('');
+ }
}
/**
diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
index 0b0c27485652..0383a3779b3e 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php
@@ -91,20 +91,6 @@ class ViewSubscriber implements EventSubscriberInterface {
return $response;
}
- public function onIframeUpload(GetResponseForControllerResultEvent $event) {
- $response = $event->getResponse();
-
- // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
- // and Skype's Browser Highlighter, convert URLs, phone numbers, etc. into
- // links. This corrupts the JSON response. Protect the integrity of the
- // JSON data by making it the value of a textarea.
- // @see http://malsup.com/jquery/form/#file-upload
- // @see http://drupal.org/node/1009382
- $html = '';
-
- return new Response($html);
- }
-
/**
* Registers the methods in this class that should be listeners.
*
diff --git a/core/lib/Drupal/Core/Render/MainContent/MainContentRenderersPass.php b/core/lib/Drupal/Core/Render/MainContent/MainContentRenderersPass.php
index 08b4665a9e9b..b8fd7547ba7c 100644
--- a/core/lib/Drupal/Core/Render/MainContent/MainContentRenderersPass.php
+++ b/core/lib/Drupal/Core/Render/MainContent/MainContentRenderersPass.php
@@ -23,9 +23,11 @@ class MainContentRenderersPass implements CompilerPassInterface {
*/
public function process(ContainerBuilder $container) {
$main_content_renderers = [];
- foreach ($container->findTaggedServiceIds('render.main_content_renderer') as $id => $attributes) {
- $format = $attributes[0]['format'];
- $main_content_renderers[$format] = $id;
+ foreach ($container->findTaggedServiceIds('render.main_content_renderer') as $id => $attributes_list) {
+ foreach ($attributes_list as $attributes) {
+ $format = $attributes['format'];
+ $main_content_renderers[$format] = $id;
+ }
}
$container->setParameter('main_content_renderers', $main_content_renderers);
}
diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php
index 31522202df08..d5f2fc1f7cb8 100644
--- a/core/modules/rest/src/Tests/ReadTest.php
+++ b/core/modules/rest/src/Tests/ReadTest.php
@@ -92,8 +92,7 @@ class ReadTest extends RESTTestBase {
// and hence when there is no matching REST route, the non-REST route is
// used, but it can't render into application/hal+json, so it returns a 406.
$this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.');
- $expected_message = '{"message":"Not Acceptable.","supported_mime_types":["text\\/html","application\\/vnd.drupal-ajax","application\\/vnd.drupal-dialog","application\\/vnd.drupal-modal"]}';
- $this->assertIdentical($expected_message, $response);
+ $this->assertTrue(strpos($response, '{"message":"Not Acceptable.","supported_mime_types":') !== FALSE);
}
/**
diff --git a/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php b/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php
index 0ddb9a4941dc..19bbe8dd358a 100644
--- a/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php
+++ b/core/tests/Drupal/Tests/Core/Ajax/AjaxResponseTest.php
@@ -8,7 +8,9 @@
namespace Drupal\Tests\Core\Ajax;
use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Render\Element\Ajax;
use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
/**
* @coversDefaultClass \Drupal\Core\Ajax\AjaxResponse
@@ -68,5 +70,21 @@ class AjaxResponseTest extends UnitTestCase {
$this->assertSame($commands[0], array('command' => 'three', 'class' => 'test-class'));
}
+ /**
+ * Tests the support for IE specific headers in file uploads.
+ *
+ * @cover ::prepareResponse
+ */
+ public function testPrepareResponseForIeFormRequestsWithFileUpload() {
+ $request = Request::create('/example', 'POST');
+ $request->headers->set('Accept', 'text/html');
+ $response = new AjaxResponse([]);
+ $response->headers->set('Content-Type', 'application/json; charset=utf-8');
+
+ $response->prepare($request);
+ $this->assertEquals('text/html; charset=utf-8', $response->headers->get('Content-Type'));
+ $this->assertEquals($response->getContent(), '');
+ }
+
}