diff --git a/includes/common.inc b/includes/common.inc index c97704c..e87e65d 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -4396,8 +4396,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; + } } } @@ -4455,14 +4457,30 @@ 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 dispalyed in JSON format, to allow for CSP + // (content security policy). This should be set to FALSE by default + // to allow BC. + 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'])); + $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 0000000..dae2c2a --- /dev/null +++ b/misc/drupal-settings-loader.js @@ -0,0 +1,13 @@ +/** + * @file + * Parse inline JSON and initialize the drupal.settings global object. + */ +(function ($) { + + 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 768fead..7f20d63 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 08452f3..263b470 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -2932,8 +2932,16 @@ class DrupalWebTestCase extends DrupalTestCase { $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 92aefe4..a9362c1 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -1520,6 +1520,11 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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 @@ -1528,8 +1533,15 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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() @@ -1542,6 +1554,12 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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() @@ -1557,6 +1575,12 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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() @@ -1569,6 +1593,12 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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() @@ -1583,6 +1613,11 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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 @@ -1596,6 +1631,11 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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 @@ -1610,7 +1650,12 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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() @@ -1627,7 +1672,12 @@ class JavaScriptTestCase extends DrupalWebTestCase { $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'); } /**