diff --git a/includes/common.inc b/includes/common.inc
index da8996a1b9..0ad15cf86b 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -4418,8 +4418,10 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
   // Filter out elements of the given scope.
   $items = array();
   foreach ($javascript as $key => $item) {
-    if ($item['scope'] == $scope) {
-      $items[$key] = $item;
+    if (array_key_exists('scope', $item)) {
+      if ($item['scope'] == $scope) {
+        $items[$key] = $item;
+      }
     }
   }
 
@@ -4477,14 +4479,32 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
     ),
   );
   foreach ($items as $item) {
-    $query_string =  empty($item['version']) ? $default_query_string : $js_version_string . $item['version'];
+    $query_string = empty($item['version']) ? $default_query_string : $js_version_string . $item['version'];
 
     switch ($item['type']) {
       case 'setting':
         $js_element = $element;
-        $js_element['#value_prefix'] = $embed_prefix;
-        $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(drupal_array_merge_deep_array($item['data'])) . ");";
-        $js_element['#value_suffix'] = $embed_suffix;
+        // The "drupal_settings_json" variable determines if drupalSettings
+        // should be displayed in JSON format, to allow for CSP (content
+        // security policy). This should be set to FALSE by default to allow for
+        // backwards compatibility.
+        if (variable_get('drupal_settings_json', FALSE)) {
+          $js_element['#attributes'] = array(
+            // This type attribute prevents this from being parsed as an inline
+            // script.
+            'type' => 'application/json',
+            'data-drupal-selector' => 'drupal-settings-json',
+          );
+          $js_element['#value'] = drupal_json_encode(drupal_array_merge_deep_array($item['data']));
+          $output .= theme('html_tag', array('element' => $js_element));
+          $js_element = $element;
+          $js_element['#attributes']['src'] = file_create_url('misc/drupal-settings-loader.js') . $query_string_separator . REQUEST_TIME;
+        }
+        else {
+          $js_element['#value_prefix'] = $embed_prefix;
+          $js_element['#value'] = 'jQuery.extend(Drupal.settings, ' . drupal_json_encode(drupal_array_merge_deep_array($item['data'])) . ");";
+          $js_element['#value_suffix'] = $embed_suffix;
+        }
         $output .= theme('html_tag', array('element' => $js_element));
         break;
 
diff --git a/misc/drupal-settings-loader.js b/misc/drupal-settings-loader.js
new file mode 100644
index 0000000000..dc6f343883
--- /dev/null
+++ b/misc/drupal-settings-loader.js
@@ -0,0 +1,15 @@
+/**
+ * @file
+ * Parse inline JSON and initialize the drupal.settings global object.
+ */
+
+(function ($) {
+  use strict;
+
+  var settingsElement = document.querySelector('script[type="application/json"][data-drupal-selector="drupal-settings-json"]');
+
+  if (settingsElement !== null) {
+    DrupalSettings = JSON.parse(settingsElement.textContent);
+    jQuery.extend(Drupal.settings, DrupalSettings);
+  }
+})(jQuery);
diff --git a/modules/locale/locale.module b/modules/locale/locale.module
index 768fead676..7f20d631e2 100644
--- a/modules/locale/locale.module
+++ b/modules/locale/locale.module
@@ -908,15 +908,17 @@ function locale_js_alter(&$javascript) {
   require_once DRUPAL_ROOT . '/includes/locale.inc';
 
   foreach ($javascript as $item) {
-    if ($item['type'] == 'file') {
-      $files = TRUE;
-      $filepath = $item['data'];
-      if (!in_array($filepath, $parsed)) {
-        // Don't parse our own translations files.
-        if (substr($filepath, 0, strlen($dir)) != $dir) {
-          _locale_parse_js_file($filepath);
-          $parsed[] = $filepath;
-          $new_files = TRUE;
+    if (array_key_exists('type', $item)) {
+      if ($item['type'] == 'file') {
+        $files = TRUE;
+        $filepath = $item['data'];
+        if (!in_array($filepath, $parsed)) {
+          // Don't parse our own translations files.
+          if (substr($filepath, 0, strlen($dir)) != $dir) {
+            _locale_parse_js_file($filepath);
+            $parsed[] = $filepath;
+            $new_files = TRUE;
+          }
         }
       }
     }
diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php
index 51407991dd..ac59a24e71 100644
--- a/modules/simpletest/drupal_web_test_case.php
+++ b/modules/simpletest/drupal_web_test_case.php
@@ -2934,8 +2934,16 @@ protected function drupalSetContent($content, $url = 'internal:') {
     $this->plainTextContent = FALSE;
     $this->elements = FALSE;
     $this->drupalSettings = array();
-    if (preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $content, $matches)) {
-      $this->drupalSettings = drupal_json_decode($matches[1]);
+
+    if (variable_get('drupal_settings_json')) {
+      if (preg_match('/\{(?:[^{}]|(?R))*\}/x', $content, $matches)) {
+        $this->drupalSettings = drupal_json_decode($matches[0]);
+      }
+    }
+    else {
+      if (preg_match('/jQuery\.extend\(Drupal\.settings, (.*?)\);/', $content, $matches)) {
+       $this->drupalSettings = drupal_json_decode($matches[1]);
+      }
     }
   }
 
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index 0f991c3c74..3b6d1d26c8 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -1545,6 +1545,11 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertRaw('misc/drupal.js', 'Default behavior: The front page of the site includes drupal.js.');
     $this->assertRaw('Drupal.settings', 'Default behavior: The front page of the site includes Drupal settings.');
     $this->assertRaw('basePath', 'Default behavior: The front page of the site includes the basePath Drupal setting.');
+    variable_set('drupal_settings_json', TRUE);
+    $this->drupalGet('');
+    $this->assertRaw('drupal-settings-json', 'When "drupal_settings_json" is TRUE. Default behavior: The front page of the site includes Drupal settings in json format.');
+    $this->assertRaw('misc/drupal-settings-loader.js', 'When "drupal_settings_json" is TRUE. Default behavior: The front page of the site includes Drupal settings loader.');
+    variable_del('drupal_settings_json');
 
     // The default front page should not use jQuery and other standard scripts
     // and settings when the 'javascript_always_use_jquery' variable is set to
@@ -1553,8 +1558,15 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->drupalGet('');
     $this->assertNoRaw('misc/jquery.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include jquery.js.');
     $this->assertNoRaw('misc/drupal.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include drupal.js.');
+    $this->assertNoRaw('drupal-settings-json', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings in json format.');
+    $this->assertNoRaw('misc/drupal-settings-loader.js', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings loader.');
     $this->assertNoRaw('Drupal.settings', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include Drupal settings.');
     $this->assertNoRaw('basePath', 'When "javascript_always_use_jquery" is FALSE: The front page of the site does not include the basePath Drupal setting.');
+    variable_set('drupal_settings_json', TRUE);
+    $this->drupalGet('');
+    $this->assertNoRaw('drupal-settings-json', 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The front page of the site does not include Drupal settings in json format.');
+    $this->assertNoRaw('misc/drupal-settings-loader.js', 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The front page of the site does not include Drupal settings loader.');
+    variable_del('drupal_settings_json');
     variable_del('javascript_always_use_jquery');
 
     // When only settings have been added via drupal_add_js(), drupal_get_js()
@@ -1567,6 +1579,12 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes the added Drupal settings.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE);
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') !== FALSE, 'When "drupal_settings_json" is TRUE. Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') !== FALSE, '"drupal_settings_json" is TRUE. Default behavior: The JavaScript returned by drupal_get_js() when only settings have been added includes drupal-settings-loader.js.');
+    variable_del('drupal_settings_json');
 
     // When only settings have been added via drupal_add_js() and the
     // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
@@ -1582,6 +1600,12 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'testJavaScriptSetting') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when only settings have been added does not include the added Drupal settings.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') === FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when only settings have been added does not include Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when only settings have been added does not include drupal-settings-loader.js.');
+    $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when only settings have been added does not include Drupal.settings.');
+    variable_del('drupal_settings_json');
     variable_del('javascript_always_use_jquery');
 
     // When a regular file has been added via drupal_add_js(), drupal_get_js()
@@ -1594,6 +1618,12 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "drupal_settings_json" is TRUE. The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added does not includes Drupal.settings.');
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') !== FALSE, 'When "drupal_settings_json" is TRUE. The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') !== FALSE, ' When "drupal_settings_json" is TRUE. The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal-settings-loader.js.');
+    variable_del('drupal_settings_json');
 
     // When a regular file has been added via drupal_add_js() and the
     // 'javascript_always_use_jquery' variable is set to FALSE, drupal_get_js()
@@ -1608,6 +1638,11 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes the custom file.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') !== FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when a custom JavaScript file has been added includes drupal-settings-loader.js.');
+    variable_del('drupal_settings_json');
     variable_del('javascript_always_use_jquery');
 
     // When a file that does not require jQuery has been added via
@@ -1621,6 +1656,11 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'Default behavior: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') !== FALSE, 'When "drupal_settings_json" is TRUE. The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') !== FALSE, 'When "drupal_settings_json" is TRUE. The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes drupal-settings-loader.js.');
+    variable_del('drupal_settings_json');
 
     // When a file that does not require jQuery has been added via
     // drupal_add_js() and the 'javascript_always_use_jquery' variable is set
@@ -1635,7 +1675,12 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'Drupal.settings') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include Drupal.settings.');
     $this->assertTrue(strpos($javascript, 'basePath') === FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added includes the custom file.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') === FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not include Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') === FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when a custom JavaScript file that does not require jQuery has been added does not includes the drupal-settings-loader.js.');
     variable_del('javascript_always_use_jquery');
+    variable_del('drupal_settings_json');
 
     // When 'javascript_always_use_jquery' is set to FALSE and a file that does
     // not require jQuery is added, followed by one that does, drupal_get_js()
@@ -1652,7 +1697,12 @@ function testJavaScriptAlwaysUseJQuery() {
     $this->assertTrue(strpos($javascript, 'basePath') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the basePath Drupal setting.');
     $this->assertTrue(strpos($javascript, 'misc/collapse.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the first custom file.');
     $this->assertTrue(strpos($javascript, 'misc/ajax.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes the second custom file.');
+    variable_set('drupal_settings_json', TRUE);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'drupal-settings-json') !== FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes Drupal.settings in json format.');
+    $this->assertTrue(strpos($javascript, 'misc/drupal-settings-loader.js') !== FALSE, 'When "javascript_always_use_jquery" is FALSE and "drupal_settings_json" is TRUE: The JavaScript returned by drupal_get_js() when at least one custom JavaScript file that requires jQuery has been added includes drupal-settings-loader.js.');
     variable_del('javascript_always_use_jquery');
+    variable_del('drupal_settings_json');
   }
 
   /**
