MDL-39810 Files: IE doesn't recognize JSON MIME Type (RFC4627).
authorMatteo Scaramuccia <moodle@matteoscaramuccia.com>
Tue, 11 Jun 2013 18:15:42 +0000 (20:15 +0200)
committerMatteo Scaramuccia <moodle@matteoscaramuccia.com>
Mon, 28 Oct 2013 22:34:35 +0000 (23:34 +0100)
Affected versions:
- IE 8+, under Compatibility View.

lib/classes/useragent.php
lib/outputrenderers.php
lib/tests/useragent_test.php

index 7b92d23..3782b5c 100644 (file)
@@ -475,12 +475,13 @@ class core_useragent {
     }
 
     /**
-     * Checks the user agent is IE and that the version is equal to or greater than that specified.
+     * Checks the user agent is IE and returns its main properties:
+     * - browser version;
+     * - whether running in compatibility view.
      *
-     * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
-     * @return bool
+     * @return bool|array False if not IE, otherwise an associative array of properties.
      */
-    public static function check_ie_version($version = null) {
+    public static function check_ie_properties() {
         // Internet Explorer.
         $useragent = self::get_user_agent_string();
         if ($useragent === false) {
@@ -490,12 +491,6 @@ class core_useragent {
             // Reject Opera.
             return false;
         }
-        // In case of IE we have to deal with BC of the version parameter.
-        if (is_null($version)) {
-            $version = 5.5; // Anything older is not considered a browser at all!
-        }
-        // IE uses simple versions, let's cast it to float to simplify the logic here.
-        $version = round($version, 1);
         // See: http://www.useragentstring.com/pages/Internet%20Explorer/.
         if (preg_match("/MSIE ([0-9\.]+)/", $useragent, $match)) {
             $browser = $match[1];
@@ -505,13 +500,62 @@ class core_useragent {
         } else {
             return false;
         }
+        $compat_view = false;
         // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
         // the Trident should always describe the capabilities of IE in any emulation mode.
         if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $useragent, $match)) {
+            $compat_view = true;
             $browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions.
         }
         $browser = round($browser, 1);
-        return ($browser >= $version);
+        return array(
+            'version'    => $browser,
+            'compatview' => $compat_view
+        );
+    }
+
+    /**
+     * Checks the user agent is IE and that the version is equal to or greater than that specified.
+     *
+     * @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
+     * @return bool
+     */
+    public static function check_ie_version($version = null) {
+        // Internet Explorer.
+        $properties = self::check_ie_properties();
+        if (!is_array($properties)) {
+            return false;
+        }
+        // In case of IE we have to deal with BC of the version parameter.
+        if (is_null($version)) {
+            $version = 5.5; // Anything older is not considered a browser at all!
+        }
+        // IE uses simple versions, let's cast it to float to simplify the logic here.
+        $version = round($version, 1);
+        return ($properties['version'] >= $version);
+    }
+
+    /**
+     * Checks the user agent is IE and that IE is running under Compatibility View setting.
+     *
+     * @return bool true if internet explorer runs in Compatibility View mode.
+     */
+    public static function check_ie_compatibility_view() {
+        // IE User Agent string when in Compatibility View:
+        // - IE  8: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; ...)".
+        // - IE  9: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; ...)".
+        // - IE 10: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; ...)".
+        // - IE 11: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; ...)".
+        // Refs:
+        // - http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx.
+        // - http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx.
+        // - http://blogs.msdn.com/b/ie/archive/2011/04/15/the-ie10-user-agent-string.aspx.
+        // - http://msdn.microsoft.com/en-us/library/ie/hh869301%28v=vs.85%29.aspx.
+        $properties = self::check_ie_properties();
+        if (!is_array($properties)) {
+            return false;
+        }
+        return $properties['compatview'];
     }
 
     /**
@@ -832,4 +876,27 @@ class core_useragent {
         }
         return $instance->supportssvg;
     }
+
+    /**
+     * Returns true if the user agent supports the MIME media type for JSON text, as defined in RFC 4627.
+     *
+     * @return bool
+     */
+    public static function supports_json_contenttype() {
+        // Modern browsers other than IE correctly supports 'application/json' media type.
+        if (!self::is_ie()) {
+            return true;
+        }
+
+        // IE8+ supports 'application/json' media type, when NOT in Compatibility View mode.
+        // Refs:
+        // - http://blogs.msdn.com/b/ie/archive/2008/09/10/native-json-in-ie8.aspx;
+        // - MDL-39810: issues when using 'text/plain' in Compatibility View for the body of an HTTP POST response.
+        if (self::check_ie_version(8) && !self::check_ie_compatibility_view()) {
+            return true;
+        }
+
+        // This browser does not support json.
+        return false;
+    }
 }
index 56b6f56..39a7cec 100644 (file)
@@ -3485,6 +3485,12 @@ class core_renderer_ajax extends core_renderer {
         // unfortunately YUI iframe upload does not support application/json
         if (!empty($_FILES)) {
             @header('Content-type: text/plain; charset=utf-8');
+            if (!core_useragent::supports_json_contenttype()) {
+                @header('X-Content-Type-Options: nosniff');
+            }
+        } else if (!core_useragent::supports_json_contenttype()) {
+            @header('Content-type: text/plain; charset=utf-8');
+            @header('X-Content-Type-Options: nosniff');
         } else {
             @header('Content-type: application/json; charset=utf-8');
         }
index 87063f0..ddfadb8 100644 (file)
@@ -67,6 +67,9 @@ class core_useragent_testcase extends basic_testcase {
             '11.0' => array(
                 'Windows 8.1' => 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0)'
             ),
+            '11.0i' => array(
+                'Windows 8.1' => ' Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)'
+            ),
         ),
         'Firefox' => array(
             '1.0.6' => array(
@@ -218,6 +221,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::is_ie());
         $this->assertTrue(core_useragent::check_ie_version());
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
+        $this->assertFalse(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('7.0'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['5.0']['Windows 98']);
@@ -225,6 +229,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertFalse(core_useragent::check_ie_version());
         $this->assertTrue(core_useragent::check_ie_version(0));
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
+        $this->assertFalse(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('7.0'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['9.0']['Windows 7']);
@@ -233,6 +238,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::check_ie_version(0));
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
         $this->assertTrue(core_useragent::check_ie_version('9.0'));
+        $this->assertFalse(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('10'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['9.0i']['Windows 7']);
@@ -241,6 +247,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::check_ie_version(0));
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
         $this->assertTrue(core_useragent::check_ie_version('9.0'));
+        $this->assertTrue(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('10'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['10.0']['Windows 8']);
@@ -250,6 +257,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
         $this->assertTrue(core_useragent::check_ie_version('9.0'));
         $this->assertTrue(core_useragent::check_ie_version('10'));
+        $this->assertFalse(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('11'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['10.0i']['Windows 8']);
@@ -259,6 +267,7 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::check_ie_version('5.0'));
         $this->assertTrue(core_useragent::check_ie_version('9.0'));
         $this->assertTrue(core_useragent::check_ie_version('10'));
+        $this->assertTrue(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('11'));
 
         core_useragent::instance(true, $this->user_agents['MSIE']['11.0']['Windows 8.1']);
@@ -269,6 +278,18 @@ class core_useragent_testcase extends basic_testcase {
         $this->assertTrue(core_useragent::check_ie_version('9.0'));
         $this->assertTrue(core_useragent::check_ie_version('10'));
         $this->assertTrue(core_useragent::check_ie_version('11'));
+        $this->assertFalse(core_useragent::check_ie_compatibility_view());
+        $this->assertFalse(core_useragent::check_ie_version('12'));
+
+        core_useragent::instance(true, $this->user_agents['MSIE']['11.0i']['Windows 8.1']);
+        $this->assertTrue(core_useragent::is_ie());
+        $this->assertTrue(core_useragent::check_ie_version());
+        $this->assertTrue(core_useragent::check_ie_version(0));
+        $this->assertTrue(core_useragent::check_ie_version('5.0'));
+        $this->assertTrue(core_useragent::check_ie_version('9.0'));
+        $this->assertTrue(core_useragent::check_ie_version('10'));
+        $this->assertTrue(core_useragent::check_ie_version('11'));
+        $this->assertTrue(core_useragent::check_ie_compatibility_view());
         $this->assertFalse(core_useragent::check_ie_version('12'));
 
         core_useragent::instance(true, $this->user_agents['Firefox']['2.0']['Windows XP']);