Commit 2d3266e6 authored by catch's avatar catch

Issue #2361681 by Wim Leers: drupal_render(): invert second argument...

Issue #2361681 by Wim Leers: drupal_render(): invert second argument ($is_recursive_call -> $is_root_call) => more strict, better DX/TX.
parent abcfdc17
......@@ -104,7 +104,7 @@ function authorize_access_allowed() {
'#theme' => 'authorize_report',
'#messages' => $results['messages'],
);
$output = drupal_render($authorize_report);
$output = drupal_render_root($authorize_report);
$links = array();
if (is_array($results['tasks'])) {
......@@ -122,7 +122,7 @@ function authorize_access_allowed() {
'#items' => $links,
'#title' => t('Next steps'),
);
$output .= drupal_render($item_list);
$output .= drupal_render_root($item_list);
}
// If a batch is running, let it run.
elseif ($request->query->has('batch')) {
......@@ -135,7 +135,7 @@ function authorize_access_allowed() {
elseif (!$batch = batch_get()) {
// We have a batch to process, show the filetransfer form.
$elements = \Drupal::formBuilder()->getForm('Drupal\Core\FileTransfer\Form\FileTransferAuthorizeForm');
$output = drupal_render($elements);
$output = drupal_render_root($elements);
}
}
// We defer the display of messages until all operations are done.
......
......@@ -2503,6 +2503,29 @@ function drupal_prepare_page($page) {
return $page;
}
/**
* Renders final HTML given a structured array tree.
*
* Calls drupal_render() in such a way that #post_render_cache callbacks are
* applied.
*
* Should therefore only be used in occasions where the final rendering is
* happening, just before sending a Response:
* - system internals that are responsible for rendering the final HTML
* - render arrays for non-HTML responses, such as feeds
*
* @param array $elements
* The structured array describing the data to be rendered.
*
* @return string
* The rendered HTML.
*
* @see drupal_render()
*/
function drupal_render_root(&$elements) {
return drupal_render($elements, TRUE);
}
/**
* Renders HTML given a structured array tree.
*
......@@ -2679,18 +2702,29 @@ function drupal_prepare_page($page) {
*
* @param array $elements
* The structured array describing the data to be rendered.
* @param bool $is_recursive_call
* Whether this is a recursive call or not, for internal use.
* @param bool $is_root_call
* (Internal use only.) Whether this is a recursive call or not. See
* drupal_render_root().
*
* @return string
* The rendered HTML.
*
* @throws \LogicException
* If a root call to drupal_render() does not result in an empty stack, this
* indicates an erroneous drupal_render() root call (a root call within a root
* call, which makes no sense). Therefore, a logic exception is thrown.
* @throws \Exception
* If a #pre_render callback throws an exception, it is caught to reset the
* stack used for bubbling rendering metadata, and then the exception is re-
* thrown.
*
* @see element_info()
* @see _theme()
* @see drupal_process_states()
* @see drupal_process_attached()
* @see drupal_render_root()
*/
function drupal_render(&$elements, $is_recursive_call = FALSE) {
function drupal_render(&$elements, $is_root_call = FALSE) {
static $stack;
$update_stack = function(&$element) use (&$stack) {
......@@ -2705,9 +2739,9 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
$bubble_stack = function() use (&$stack) {
// If there's only one frame on the stack, then this is the root call, and
// we can't bubble up further.
// we can't bubble up further. Reset the stack for the next root call.
if ($stack->count() === 1) {
$stack->pop();
$stack = NULL;
return;
}
......@@ -2749,10 +2783,10 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
$cached_element = drupal_render_cache_get($elements);
if ($cached_element !== FALSE) {
$elements = $cached_element;
// Only when we're not in a recursive drupal_render() call,
// Only when we're not in a root (non-recursive) drupal_render() call,
// #post_render_cache callbacks must be executed, to prevent breaking the
// render cache in case of nested elements with #cache set.
if (!$is_recursive_call) {
if ($is_root_call) {
_drupal_render_process_post_render_cache($elements);
}
$elements['#markup'] = SafeMarkup::set($elements['#markup']);
......@@ -2779,7 +2813,23 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
if (is_string($callable) && strpos($callable, '::') === FALSE) {
$callable = $controller_resolver->getControllerFromDefinition($callable);
}
$elements = call_user_func($callable, $elements);
// Since #pre_render callbacks may be used for generating a render array's
// content, and we might be rendering the main content for the page, it is
// possible that a #pre_render callback throws an exception that will
// cause a different page to be rendered (e.g. throwing
// \Symfony\Component\HttpKernel\Exception\NotFoundHttpException will
// cause the 404 page to be rendered). That page might also use
// drupal_render(), but if exceptions aren't caught here, the stack will
// be left in an inconsistent state.
// Hence, catch all exceptions and reset the stack and re-throw them.
try {
$elements = call_user_func($callable, $elements);
}
catch (\Exception $e) {
// Reset stack and re-throw exception.
$stack = NULL;
throw $e;
}
}
}
......@@ -2838,7 +2888,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
// process as drupal_render_children() but is inlined for speed.
if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) {
foreach ($children as $key) {
$elements['#children'] .= drupal_render($elements[$key], TRUE);
$elements['#children'] .= drupal_render($elements[$key]);
}
$elements['#children'] = SafeMarkup::set($elements['#children']);
}
......@@ -2912,7 +2962,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
drupal_render_cache_set($elements['#markup'], $elements);
}
// Only when we're not in a recursive drupal_render() call,
// Only when we're in a root (non-recursive) drupal_render() call,
// #post_render_cache callbacks must be executed, to prevent breaking the
// render cache in case of nested elements with #cache set.
//
......@@ -2921,7 +2971,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
// - they run when #cache is enabled and there is a cache miss.
// Only the case of a cache hit when #cache is enabled, is not handled here,
// that is handled earlier in drupal_render().
if (!$is_recursive_call) {
if ($is_root_call) {
// We've already called $update_stack() earlier, which updated both the
// element and current stack frame. However,
// _drupal_render_process_post_render_cache() can both change the element
......@@ -2934,6 +2984,9 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
$elements['#cache']['tags'] = Cache::mergeTags($elements['#cache']['tags'], $post_render_additions->tags);
$elements['#attached'] = drupal_merge_attached($elements['#attached'], $post_render_additions->attached);
$elements['#post_render_cache'] = NestedArray::mergeDeep($elements['#post_render_cache'], $post_render_additions->postRenderCache);
if ($stack->count() !== 1) {
throw new \LogicException('A stray drupal_render() invocation with $is_root_call = TRUE is causing bubbling of attached assets to break.');
}
}
// Rendering is finished, all necessary info collected!
......@@ -2998,7 +3051,7 @@ function render(&$element) {
return $element['#markup'];
}
show($element);
return drupal_render($element, TRUE);
return drupal_render($element);
}
else {
// Safe-guard for inappropriate use of render() on flat variables: return
......
......@@ -378,7 +378,7 @@ function _theme($hook, $variables = array()) {
// that we're preprocessing variables for.
if (isset($variables['#attached'])) {
$preprocess_attached = ['#attached' => $variables['#attached']];
drupal_render($preprocess_attached, TRUE);
drupal_render($preprocess_attached);
}
}
......
......@@ -58,14 +58,14 @@ public function render($content) {
}
}
$html = $this->drupalRender($content);
$html = $this->drupalRenderRoot($content);
// The selector for the insert command is NULL as the new content will
// replace the element making the Ajax call. The default 'replaceWith'
// behavior can be changed with #ajax['method'].
$response->addCommand(new InsertCommand(NULL, $html));
$status_messages = array('#theme' => 'status_messages');
$output = $this->drupalRender($status_messages);
$output = $this->drupalRenderRoot($status_messages);
if (!empty($output)) {
$response->addCommand(new PrependCommand(NULL, $output));
}
......@@ -73,12 +73,12 @@ public function render($content) {
}
/**
* Wraps drupal_render().
* Wraps drupal_render_root().
*
* @todo: Remove as part of https://drupal.org/node/2182149
*/
protected function drupalRender(&$elements, $is_recursive_call = FALSE) {
$output = drupal_render($elements, $is_recursive_call);
protected function drupalRenderRoot(&$elements) {
$output = drupal_render_root($elements);
drupal_process_attached($elements);
return $output;
}
......
......@@ -96,7 +96,7 @@ public function dialog(Request $request, RouteMatchInterface $route_match, $_con
);
}
$content = drupal_render($page_content);
$content = drupal_render_root($page_content);
drupal_process_attached($page_content);
$title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
$response = new AjaxResponse();
......
......@@ -79,7 +79,7 @@ public function view(EntityInterface $_entity, $view_mode = 'full', $langcode =
$build = $this->entityManager->getTranslationFromContext($_entity)
->get($label_field)
->view($view_mode);
$page['#title'] = drupal_render($build, TRUE);
$page['#title'] = drupal_render($build);
}
}
......
......@@ -116,7 +116,7 @@ public function onView(GetResponseForControllerResultEvent $event) {
$page_result['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
}
$event->setResponse(new Response(drupal_render($page_result)));
$event->setResponse(new Response(drupal_render_root($page_result)));
}
}
......
......@@ -55,9 +55,9 @@ public function render(HtmlFragmentInterface $fragment, $status_code = 200) {
// Build the HtmlPage object.
$page = new HtmlPage('', array(), $fragment->getTitle());
$page = $this->preparePage($page, $page_array);
$page->setBodyTop(drupal_render($page_array['page_top']));
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
$page->setContent(drupal_render($page_array));
$page->setBodyTop(drupal_render_root($page_array['page_top']));
$page->setBodyBottom(drupal_render_root($page_array['page_bottom']));
$page->setContent(drupal_render_root($page_array));
$page->setStatusCode($status_code);
drupal_process_attached($page_array);
......
......@@ -108,9 +108,9 @@ public static function renderPage($main, $title = '', $theme = 'maintenance', ar
// available in hook_page_alter(), so that HTML attributes can be altered.
$page = \Drupal::service('html_fragment_renderer')->preparePage($page, $page_array);
$page->setBodyTop(drupal_render($page_array['page_top']));
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
$page->setContent(drupal_render($page_array));
$page->setBodyTop(drupal_render_root($page_array['page_top']));
$page->setBodyBottom(drupal_render_root($page_array['page_bottom']));
$page->setContent(drupal_render_root($page_array));
drupal_process_attached($page_array);
if (isset($page_array['page_top'])) {
drupal_process_attached($page_array['page_top']);
......
......@@ -39,7 +39,7 @@ public function __construct(UrlGeneratorInterface $url_generator) {
* {@inheritdoc}
*/
public function render(array $render_array) {
$content = $this->drupalRender($render_array);
$content = $this->drupalRenderRoot($render_array);
if (!empty($render_array)) {
drupal_process_attached($render_array);
}
......@@ -78,11 +78,11 @@ public function render(array $render_array) {
}
/**
* Wraps drupal_render().
* Wraps drupal_render_root().
*
* @todo: Convert drupal_render into a proper injectable service.
* @todo: Convert drupal_render_root into a proper injectable service.
*/
protected function drupalRender(&$elements, $is_recursive_call = FALSE) {
return drupal_render($elements, $is_recursive_call);
protected function drupalRenderRoot(&$elements) {
return drupal_render_root($elements);
}
}
......@@ -103,7 +103,7 @@ public function render($row) {
'#options' => $this->options,
'#row' => $item,
);
return drupal_render($build);
return drupal_render_root($build);
}
}
......@@ -96,3 +96,78 @@ function _ckeditor_theme_css($theme = NULL) {
}
return $css;
}
/**
* Implements hook_ENTITY_TYPE_update().
*
* Recalculates the 'format_tags' CKEditor setting when a text format is added.
*
* @see \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal::generateFormatTagsSetting()
* @see ckeditor_rebuild()
*/
function ckeditor_filter_format_insert() {
ckeditor_rebuild();
}
/**
* Implements hook_ENTITY_TYPE_update().
*
* Recalculates the 'format_tags' CKEditor setting when a text format changes.
*
* @see \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal::generateFormatTagsSetting()
* @see ckeditor_rebuild()
*/
function ckeditor_filter_format_update() {
ckeditor_rebuild();
}
/**
* Implements hook_rebuild().
*
* Calculates the 'format_tags' CKEditor setting for each text format.
*
* If this wouldn't happen in hook_rebuild(), then the first drupal_render()
* call that occurs for a page that contains a #type 'text_format' element will
* cause the CKEditor::getJSSettings() to be called, which will cause
* Internal::generateFormatTagsSetting() to be called, which calls
* check_markup(), which finally calls drupal_render() non-recursively, because
* a filter might apply #post_render_cache callbacks.
* This would be a root call inside a root call, which breaks the stack-based
* logic for bubbling rendering metadata.
* Therefore this pre-calculates the needed values, and hence performs the
* check_markup() calls outside of a drupal_render() call tree.
*
* @see \Drupal\ckeditor\Plugin\CKEditorPlugin\Internal::generateFormatTagsSetting()
* @see ckeditor_filter_format_insert()
* @see ckeditor_filter_format_update()
*/
function ckeditor_rebuild() {
/** @var \Drupal\filter\FilterFormatInterface[] $formats */
$formats = filter_formats();
foreach ($formats as $format) {
$key = 'ckeditor_internal_format_tags:' . $format->id();
// The <p> tag is always allowed — HTML without <p> tags is nonsensical.
$format_tags = array('p');
// Given the list of possible format tags, automatically determine whether
// the current text format allows this tag, and thus whether it should show
// up in the "Format" dropdown.
$possible_format_tags = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre');
foreach ($possible_format_tags as $tag) {
$input = '<' . $tag . '>TEST</' . $tag . '>';
$output = trim(check_markup($input, $format->id()));
if ($input == $output) {
$format_tags[] = $tag;
}
}
$format_tags = implode(';', $format_tags);
// Cache the "format_tags" configuration. This cache item is infinitely
// valid; it only changes whenever the text format is changed, which is
// guaranteed by the hook_ENTITY_TYPE_update() and hook_ENTITY_TYPE_insert()
// hook implementations.
\Drupal::state()->set($key, $format_tags);
}
}
......@@ -302,6 +302,10 @@ public function getButtons() {
*
* @return array
* An array containing the "format_tags" configuration.
*
* @see ckeditor_rebuild()
* @see ckeditor_filter_format_insert()
* @see ckeditor_filter_format_update()
*/
protected function generateFormatTagsSetting(Editor $editor) {
// When no text format is associated yet, assume no tag is allowed.
......@@ -311,35 +315,9 @@ protected function generateFormatTagsSetting(Editor $editor) {
}
$format = $editor->getFilterFormat();
$cid = 'ckeditor_internal_format_tags:' . $format->id();
if ($cached = $this->cache->get($cid)) {
$format_tags = $cached->data;
}
else {
// The <p> tag is always allowed — HTML without <p> tags is nonsensical.
$format_tags = array('p');
// Given the list of possible format tags, automatically determine whether
// the current text format allows this tag, and thus whether it should show
// up in the "Format" dropdown.
$possible_format_tags = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre');
foreach ($possible_format_tags as $tag) {
$input = '<' . $tag . '>TEST</' . $tag . '>';
$output = trim(check_markup($input, $editor->id()));
if ($input == $output) {
$format_tags[] = $tag;
}
}
$format_tags = implode(';', $format_tags);
// Cache the "format_tags" configuration. This cache item is infinitely
// valid; it only changes whenever the text format is changed, hence it's
// tagged with the text format's cache tag.
$this->cache->set($cid, $format_tags, Cache::PERMANENT, $format->getCacheTags());
}
return $format_tags;
// The <p> tag is always allowed — HTML without <p> tags is nonsensical.
$default = 'p';
return \Drupal::state()->get('ckeditor_internal_format_tags:' . $format->id(), $default);
}
/**
......
......@@ -103,6 +103,9 @@ function testGetJSSettings() {
$this->container->get('plugin.manager.editor')->clearCachedDefinitions();
$this->ckeditor = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
$this->container->get('plugin.manager.ckeditor.plugin')->clearCachedDefinitions();
// KernelTestBase::enableModules() unfortunately doesn't invoke
// hook_rebuild() just like a "real" Drupal site would. Do it manually.
\Drupal::moduleHandler()->invoke('ckeditor', 'rebuild');
$settings = $editor->getSettings();
$settings['toolbar']['rows'][0][0]['items'][] = 'Strike';
$settings['toolbar']['rows'][0][0]['items'][] = 'Format';
......@@ -206,6 +209,11 @@ function testGetJSSettings() {
$expected_config['format_tags'] = 'p';
ksort($expected_config);
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Generated JS settings are correct for customized configuration.');
// Assert that we're robust enough to withstand people messing with State
// manually.
\Drupal::state()->delete('ckeditor_internal_format_tags:' . $format->id());
$this->assertIdentical($expected_config, $this->ckeditor->getJSSettings($editor), 'Even when somebody manually deleted the key-value pair in State with the pre-calculated format_tags setting, it returns "p" — because the <p> tag is always allowed.');
}
/**
......
......@@ -70,13 +70,12 @@ public function renderForm(array $element, array $context) {
);
$comment = $this->entityManager->getStorage('comment')->create($values);
$form = $this->entityFormBuilder->getForm($comment);
// @todo: This only works as long as assets are still tracked in a global
// static variable, see https://drupal.org/node/2238835
$markup = drupal_render($form);
$callback = 'comment.post_render_cache:renderForm';
$placeholder = drupal_render_cache_generate_placeholder($callback, $context);
$element['#markup'] = str_replace($placeholder, $markup, $element['#markup']);
$element['#attached'] = drupal_merge_attached($element['#attached'], $form['#attached']);
return $element;
}
......
......@@ -130,7 +130,7 @@ public function render($row) {
if ($view_mode != 'title') {
// We render comment contents.
$item_text .= drupal_render($build);
$item_text .= drupal_render_root($build);
}
$item = new \stdClass();
......@@ -146,7 +146,7 @@ public function render($row) {
'#options' => $this->options,
'#row' => $item,
);
return drupal_render($build);
return drupal_render_root($build);
}
}
......@@ -115,7 +115,7 @@ function contact_mail($key, &$message, $params) {
$message['subject'] .= t('[!form] !subject', $variables, $options);
$message['body'][] = t("!sender-name (!sender-url) sent a message using the contact form at !form-url.", $variables, $options);
$build = entity_view($contact_message, 'mail', $language->getId());
$message['body'][] = drupal_render($build);
$message['body'][] = drupal_render_root($build);
break;
case 'page_autoreply':
......@@ -134,7 +134,7 @@ function contact_mail($key, &$message, $params) {
$message['body'][] = t("!sender-name (!sender-url) has sent you a message via your contact form at !site-name.", $variables, $options);
$message['body'][] = t("If you don't want to receive such emails, you can change your settings at !recipient-edit-url.", $variables, $options);
$build = entity_view($contact_message, 'mail', $language->getId());
$message['body'][] = drupal_render($build);
$message['body'][] = drupal_render_root($build);
break;
}
}
......
......@@ -303,7 +303,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $filter_types_to
'#filter_types_to_skip' => $filter_types_to_skip,
'#langcode' => $langcode,
);
return drupal_render($build);
return drupal_render_root($build);
}
/**
......
......@@ -234,7 +234,7 @@ function testProcessedTextElement() {
'#text' => '<p>Hello, world!</p>',
'#format' => 'element_test',
);
drupal_render($build);
drupal_render_root($build);
// Verify the assets, cache tags and #post_render_cache callbacks.
$expected_assets = array(
......
......@@ -587,9 +587,9 @@ function template_preprocess_node(&$variables) {
$variables['node'] = $variables['elements']['#node'];
/** @var \Drupal\node\NodeInterface $node */
$node = $variables['node'];
$variables['date'] = drupal_render($variables['elements']['created'], TRUE);
$variables['date'] = drupal_render($variables['elements']['created']);
unset($variables['elements']['created']);
$variables['author_name'] = drupal_render($variables['elements']['uid'], TRUE);
$variables['author_name'] = drupal_render($variables['elements']['uid']);
unset($variables['elements']['uid']);
$variables['url'] = $node->url('canonical', array(
......
......@@ -147,7 +147,7 @@ public function render($row) {
if ($display_mode != 'title') {
// We render node contents.
$item_text .= drupal_render($build);
$item_text .= drupal_render_root($build);
}
$item = new \stdClass();
......@@ -162,7 +162,7 @@ public function render($row) {
'#options' => $this->options,
'#row' => $item,
);
return drupal_render($theme_function);
return drupal_render_root($theme_function);
}
}
......@@ -271,7 +271,7 @@ public function execute() {
parent::execute();
$output = $this->view->render();
return new Response(drupal_render($output), 200, array('Content-type' => $this->getMimeType()));
return new Response(drupal_render_root($output), 200, array('Content-type' => $this->getMimeType()));
}
/**
......
......@@ -92,7 +92,7 @@ public function testSerializerResponses() {
// Mock the request content type by setting it on the display handler.
$view->display_handler->setContentType('json');
$output = $view->preview();
$this->assertIdentical($actual_json, drupal_render($output), 'The expected JSON preview output was found.');
$this->assertIdentical($actual_json, drupal_render_root($output), 'The expected JSON preview output was found.');
// Test a 403 callback.
$this->drupalGet('test/serialize/denied');
......
......@@ -106,9 +106,9 @@ public function render(array $output, $status_code = 200) {
$page = $this->fragmentRenderer->preparePage($page, $page_array);
$page->setBodyTop(drupal_render($page_array['page_top']));
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
$page->setContent(drupal_render($page_array));
$page->setBodyTop(drupal_render_root($page_array['page_top']));
$page->setBodyBottom(drupal_render_root($page_array['page_bottom']));
$page->setContent(drupal_render_root($page_array));
drupal_process_attached($page_array);
if (isset($page_array['page_top'])) {
......
......@@ -501,7 +501,7 @@ function testDrupalRenderPostRenderCache() {
// #cache disabled.
$element = $test_element;
$element['#markup'] = '<p>#cache disabled</p>';
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
$expected_js = [
......@@ -518,7 +518,7 @@ function testDrupalRenderPostRenderCache() {
$element = $test_element;
$element['#cache'] = array('cid' => 'post_render_cache_test_GET');
$element['#markup'] = '<p>#cache enabled, GET</p>';
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertTrue(isset($element['#printed']), 'No cache hit');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
......@@ -542,7 +542,7 @@ function testDrupalRenderPostRenderCache() {
// GET request: #cache enabled, cache hit.
$element['#cache'] = array('cid' => 'post_render_cache_test_GET');
$element['#markup'] = '<p>#cache enabled, GET</p>';
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertFalse(isset($element['#printed']), 'Cache hit');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
......@@ -560,7 +560,7 @@ function testDrupalRenderPostRenderCache() {
$element = $test_element;
$element['#cache'] = array('cid' => 'post_render_cache_test_POST');
$element['#markup'] = '<p>#cache enabled, POST</p>';
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertTrue(isset($element['#printed']), 'No cache hit');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
......@@ -624,7 +624,7 @@ function testDrupalRenderChildrenPostRenderCache() {
'#markup' => 'Subchild',
);
$element = $test_element;
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertTrue(isset($element['#printed']), 'No cache hit');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
......@@ -672,7 +672,7 @@ function testDrupalRenderChildrenPostRenderCache() {
// GET request: #cache enabled, cache hit.
$element = $test_element;
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertFalse(isset($element['#printed']), 'Cache hit');
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
......@@ -681,7 +681,7 @@ function testDrupalRenderChildrenPostRenderCache() {
// Use the exact same element, but now unset #cache.
unset($test_element['#cache']);
$element = $test_element;
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
......@@ -697,7 +697,7 @@ function testDrupalRenderChildrenPostRenderCache() {
$element = $test_element;
$element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
$element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertTrue(isset($element['#printed']), 'No cache hit');
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
......@@ -768,7 +768,7 @@ function testDrupalRenderChildrenPostRenderCache() {
// GET request: #cache enabled, cache hit, parent element.
$element = $test_element;
$element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertFalse(isset($element['#printed']), 'Cache hit');
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
......@@ -777,7 +777,7 @@ function testDrupalRenderChildrenPostRenderCache() {
$element = $test_element;
$element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
$element = $element['child'];
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
$this->assertFalse(isset($element['#printed']), 'Cache hit');
$expected_js = [
......@@ -815,7 +815,7 @@ function testDrupalRenderRenderCachePlaceholder() {
// #cache disabled.
$element = $test_element;
$output = drupal_render($element);
$output = drupal_render_root($element);
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
$expected_js = [
['type' => 'setting', 'data' => ['common_test' => $context]],
......@@ -829,7 +829,7 @@ function testDrupalRenderRenderCachePlaceholder() {
// GET request: #cache enabled, cache miss.
$element = $test_element;
$element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
$output = drupal_render($element);
$output = drupal_render_root($element);