Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1031
diff -u -p -r1.1031 common.inc
--- includes/common.inc	27 Oct 2009 19:29:12 -0000	1.1031
+++ includes/common.inc	31 Oct 2009 09:19:49 -0000
@@ -4568,6 +4568,47 @@ function drupal_set_page_content($conten
 }
 
 /**
+ * #pre_render callback to render a link into #markup.
+ *
+ * Doing so during pre_render gives modules a chance to alter the link parts.
+ *
+ * @param $elements
+ *   A structured array whose keys form the arguments to l():
+ *   - #title: The link text to pass as argument to l().
+ *   - #href: The URL path component to pass as argument to l().
+ *   - #options: (optional) An array of options to pass to l().
+ *
+ * @return
+ *   The passed in elements containing a rendered link in '#markup'.
+ */
+function drupal_pre_render_link($elements) {
+  $options = isset($elements['#options']) ? $elements['#options'] : array();
+  $elements['#markup'] = l($elements['#title'], $elements['#href'], $options);
+  return $elements;
+}
+
+/**
+ * #pre_render callback to append contents in #markup to #children.
+ *
+ * This needs to be a #pre_render callback, because eventually assigned
+ * #theme_wrappers will expect the element's rendered content in #children.
+ * Note that if also a #theme is defined for the element, then the result of
+ * the theme callback will override #children.
+ *
+ * @see drupal_render()
+ *
+ * @param $elements
+ *   A structured array using the #markup key.
+ *
+ * @return
+ *   The passed in elements, but #markup appended to #children.
+ */
+function drupal_pre_render_markup($elements) {
+  $elements['#children'] = $elements['#markup'];
+  return $elements;
+}
+
+/**
  * Renders the page, including all theming.
  *
  * @param $page
@@ -4684,6 +4725,12 @@ function drupal_render(&$elements) {
   if (isset($elements['#cache']) && $cached_output = drupal_render_cache_get($elements)) {
     return $cached_output;
   }
+  
+  // If #markup is not empty, set #type. This allows to specify just #markup on
+  // an element without setting #type.
+  if (!empty($elements['#markup']) && !isset($elements['#type'])) {
+    $elements['#type'] = 'markup';
+  }
 
   // If the default values for this element have not been loaded yet, populate
   // them.
@@ -4697,12 +4744,6 @@ function drupal_render(&$elements) {
     $elements += $defaults;
   }
 
-  // If #markup is not empty and no theme function is set, use theme_markup.
-  // This allows to specify just #markup on an element without setting the #type.
-  if (!empty($elements['#markup']) && empty($elements['#theme'])) {
-    $elements['#theme'] = 'markup';
-  }
-
   // Make any final changes to the element before it is rendered. This means
   // that the $element or the children can be altered or corrected before the
   // element is rendered into the final text.
@@ -4717,7 +4758,11 @@ function drupal_render(&$elements) {
   // Get the children of the element, sorted by weight.
   $children = element_children($elements, TRUE);
 
-  $elements['#children'] = '';
+  // Initialize this element's #children, unless a #pre_render callback already
+  // preset #children.
+  if (!isset($elements['#children'])) {
+    $elements['#children'] = '';
+  }
   // Call the element's #theme function if it is set. Then any children of the
   // element have to be rendered there.
   if (isset($elements['#theme'])) {
@@ -5051,6 +5096,10 @@ function element_child($key) {
  *   The array keys of the element's children.
  */
 function element_children(&$elements, $sort = FALSE) {
+  // Early return if the element doesn't have children.
+  if (!empty($elements['#leaf'])) {
+    return array();
+  }
   // Do not attempt to sort elements which have already been sorted.
   $sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
 
@@ -5255,9 +5304,6 @@ function drupal_common_theme() {
     'textarea' => array(
       'render element' => 'element',
     ),
-    'markup' => array(
-      'render element' => 'element',
-    ),
     'password' => array(
       'render element' => 'element',
     ),
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.387
diff -u -p -r1.387 form.inc
--- includes/form.inc	27 Oct 2009 04:12:39 -0000	1.387
+++ includes/form.inc	31 Oct 2009 09:19:50 -0000
@@ -2668,24 +2668,6 @@ function theme_textarea($variables) {
 }
 
 /**
- * Theme HTML markup for use in forms.
- *
- * @param $variables
- *   An associative array containing:
- *   - element: An associative array containing the properties of the element.
- *     Properties used: #markup, #children.
- *
- * @return
- *   A themed HTML string representing the HTML markup.
- *
- * @ingroup themeable
- */
-function theme_markup($variables) {
-  $element = $variables['element'];
-  return (!empty($element['#markup']) ? $element['#markup'] : '') . drupal_render_children($element);
-}
-
-/**
  * Theme a password form element.
  *
  * @param $variables
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.233
diff -u -p -r1.233 locale.inc
--- includes/locale.inc	16 Oct 2009 02:04:42 -0000	1.233
+++ includes/locale.inc	31 Oct 2009 09:19:50 -0000
@@ -579,13 +579,13 @@ function _locale_languages_configure_for
 
       $table_form['description'][$id] = array('#markup' => filter_xss_admin($provider['description']));
 
-      $config_op = '';
+      $config_op = array();
       if (isset($provider['config'])) {
-        $config_op = l(t('Configure'), $provider['config']);
+        $config_op = array('#type' => 'link', '#title' => t('Configure'), '#href' => $provider['config']);
         // If there is at least one operation enabled show the operation column.
         $table_form['#show_operations'] = TRUE;
       }
-      $table_form['operation'][$id] = array('#markup' => $config_op);
+      $table_form['operation'][$id] = $config_op;
     }
   }
 
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.542
diff -u -p -r1.542 theme.inc
--- includes/theme.inc	23 Oct 2009 22:24:11 -0000	1.542
+++ includes/theme.inc	31 Oct 2009 09:19:50 -0000
@@ -2044,6 +2044,10 @@ function _theme_table_cell($cell, $heade
 
   if (is_array($cell)) {
     $data = isset($cell['data']) ? $cell['data'] : '';
+    // Cell's data property can be a string or a renderable array.
+    if (is_array($data)) {
+      $data = drupal_render($data);
+    }
     $header |= isset($cell['header']);
     unset($cell['data']);
     unset($cell['header']);
Index: modules/block/block.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v
retrieving revision 1.60
diff -u -p -r1.60 block.admin.inc
--- modules/block/block.admin.inc	16 Oct 2009 23:48:37 -0000	1.60
+++ modules/block/block.admin.inc	31 Oct 2009 09:19:50 -0000
@@ -87,14 +87,16 @@ function block_admin_display_form($form,
       '#options' => $block_regions,
     );
     $form[$key]['configure'] = array(
-      '#markup' => l(t('configure'),
-      'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure'),
+      '#type' => 'link',
+      '#title' => t('configure'),
+      '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure',
     );
     if ($block['module'] == 'block') {
       $form[$key]['delete'] = array(
-        '#markup' => l(t('delete'),
-        'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete'),
-      );
+        '#type' => 'link',
+        '#title' => t('delete'),
+        '#href' => 'admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/delete',
+     );
     }
   }
   // Do not allow disabling the main system content block.
Index: modules/field_ui/field_ui.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/field_ui/field_ui.admin.inc,v
retrieving revision 1.26
diff -u -p -r1.26 field_ui.admin.inc
--- modules/field_ui/field_ui.admin.inc	29 Oct 2009 07:21:04 -0000	1.26
+++ modules/field_ui/field_ui.admin.inc	31 Oct 2009 09:19:50 -0000
@@ -110,17 +110,29 @@ function field_ui_field_overview_form($f
         '#markup' => $instance['field_name'],
       ),
       'type' => array(
-        '#markup' => l(t($field_types[$field['type']]['label']), $admin_field_path . '/field-settings', array('attributes' => array('title' => t('Edit field settings.')))),
+        '#type' => 'link',
+        '#title' => t($field_types[$field['type']]['label']),
+        '#href' => $admin_field_path . '/field-settings',
+        '#options' => array('attributes' => array('title' => t('Edit field settings.'))),
       ),
       'widget_type' => array(
-        '#markup' => l(t($widget_types[$instance['widget']['type']]['label']), $admin_field_path . '/widget-type', array('attributes' => array('title' => t('Change widget type.')))),
+        '#type' => 'link',
+        '#title' => t($widget_types[$instance['widget']['type']]['label']),
+        '#href' => $admin_field_path . '/widget-type',
+        '#options' => array('attributes' => array('title' => t('Change widget type.'))),
       ),
       'edit' => array(
-        '#markup' => l(t('edit'), $admin_field_path, array('attributes' => array('title' => t('Edit instance settings.')))),
+        '#type' => 'link',
+        '#title' => t('edit'),
+        '#href' => $admin_field_path,
+        '#options' => array('attributes' => array('title' => t('Edit instance settings.'))),
       ),
       'delete' => array(
-        '#markup' => l(t('delete'), $admin_field_path . '/delete', array('attributes' => array('title' => t('Delete instance.')))),
-       ),
+        '#type' => 'link',
+        '#title' => t('delete'),
+        '#href' => $admin_field_path . '/delete',
+        '#options' => array('attributes' => array('title' => t('Delete instance.'))),
+      ),
       'weight' => array(
         '#type' => 'textfield',
         '#default_value' => $weight,
Index: modules/filter/filter.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/filter/filter.admin.inc,v
retrieving revision 1.49
diff -u -p -r1.49 filter.admin.inc
--- modules/filter/filter.admin.inc	13 Oct 2009 15:39:41 -0000	1.49
+++ modules/filter/filter.admin.inc	31 Oct 2009 09:19:50 -0000
@@ -32,8 +32,8 @@ function filter_admin_overview($form) {
       $roles_markup = $roles ? implode(', ', $roles) : t('No roles may use this format');
     }
     $form['formats'][$id]['roles'] = array('#markup' => $roles_markup);
-    $form['formats'][$id]['configure'] = array('#markup' => l(t('configure'), 'admin/config/content/formats/' . $id));
-    $form['formats'][$id]['delete'] = array('#markup' => $form['formats'][$id]['#is_fallback'] ? '' : l(t('delete'), 'admin/config/content/formats/' . $id . '/delete'));
+    $form['formats'][$id]['configure'] = array('#type' => 'link', '#title' => t('configure'), '#href' => 'admin/config/content/formats/' . $id);
+    $form['formats'][$id]['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => 'admin/config/content/formats/' . $id . '/delete', '#access' => !$form['formats'][$id]['#is_fallback']);
     $form['formats'][$id]['weight'] = array('#type' => 'weight', '#default_value' => $format->weight);
   }
   $form['submit'] = array('#type' => 'submit', '#value' => t('Save changes'));
Index: modules/image/image.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/image/image.admin.inc,v
retrieving revision 1.14
diff -u -p -r1.14 image.admin.inc
--- modules/image/image.admin.inc	16 Oct 2009 00:52:46 -0000	1.14
+++ modules/image/image.admin.inc	31 Oct 2009 09:19:51 -0000
@@ -97,11 +97,15 @@ function image_style_form($form, &$form_
       '#access' => $editable,
     );
     $form['effects'][$ieid]['configure'] = array(
-      '#markup' => isset($effect['form callback']) ? l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'] ) : '',
-      '#access' => $editable,
+      '#type' => 'link',
+      '#title' => t('edit'),
+      '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'],
+      '#access' => $editable && isset($effect['form callback']),
     );
     $form['effects'][$ieid]['remove'] = array(
-      '#markup' => l(t('delete'), 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'] . '/delete'),
+      '#type' => 'link',
+      '#title' => t('delete'),
+      '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'] . '/delete',
       '#access' => $editable,
     );
   }
Index: modules/menu/menu.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/menu/menu.admin.inc,v
retrieving revision 1.65
diff -u -p -r1.65 menu.admin.inc
--- modules/menu/menu.admin.inc	15 Oct 2009 14:07:29 -0000	1.65
+++ modules/menu/menu.admin.inc	31 Oct 2009 09:19:51 -0000
@@ -109,20 +109,16 @@ function _menu_overview_tree_form($tree)
       );
       // Build a list of operations.
       $operations = array();
-      $operations['edit'] = l(t('edit'), 'admin/structure/menu/item/' . $item['mlid'] . '/edit');
+      $operations['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/edit');
       // Only items created by the menu module can be deleted.
       if ($item['module'] == 'menu' || $item['updated'] == 1) {
-        $operations['delete'] = l(t('delete'), 'admin/structure/menu/item/' . $item['mlid'] . '/delete');
+        $operations['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/delete');
       }
       // Set the reset column.
       elseif ($item['module'] == 'system' && $item['customized']) {
-        $operations['reset'] = l(t('reset'), 'admin/structure/menu/item/' . $item['mlid'] . '/reset');
-      }
-
-      $form[$mlid]['operations'] = array();
-      foreach ($operations as $op => $value) {
-        $form[$mlid]['operations'][$op] = array('#markup' => $value);
+        $operations['reset'] = array('#type' => 'link', '#title' => t('reset'), '#href' => 'admin/structure/menu/item/' . $item['mlid'] . '/reset');
       }
+      $form[$mlid]['operations'] = $operations;
     }
 
     if ($data['below']) {
Index: modules/profile/profile.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile/profile.admin.inc,v
retrieving revision 1.33
diff -u -p -r1.33 profile.admin.inc
--- modules/profile/profile.admin.inc	9 Oct 2009 01:00:02 -0000	1.33
+++ modules/profile/profile.admin.inc	31 Oct 2009 09:19:51 -0000
@@ -26,8 +26,8 @@ function profile_admin_overview($form) {
     $form[$field->fid]['type'] = array('#markup' => $field->type);
     $form[$field->fid]['category'] = array('#type' => 'select', '#default_value' => $field->category, '#options' => array());
     $form[$field->fid]['weight'] = array('#type' => 'weight', '#default_value' => $field->weight);
-    $form[$field->fid]['edit'] = array('#markup' => l(t('edit'), "admin/config/people/profile/edit/$field->fid"));
-    $form[$field->fid]['delete'] = array('#markup' => l(t('delete'), "admin/config/people/profile/delete/$field->fid"));
+    $form[$field->fid]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => "admin/config/people/profile/edit/$field->fid");
+    $form[$field->fid]['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => "admin/config/people/profile/delete/$field->fid");
   }
 
   // Add the category combo boxes
@@ -51,6 +51,7 @@ function profile_admin_overview($form) {
   }
   $form['#tree'] = TRUE;
 
+  // @todo: Any reason this isn't done using an element with #theme = 'links'?
   $addnewfields = '<h2>' . t('Add new field') . '</h2>';
   $addnewfields .= '<ul>';
   foreach (_profile_field_types() as $key => $value) {
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.216
diff -u -p -r1.216 system.admin.inc
--- modules/system/system.admin.inc	19 Oct 2009 23:28:40 -0000	1.216
+++ modules/system/system.admin.inc	31 Oct 2009 09:19:51 -0000
@@ -240,13 +240,8 @@ function system_themes_form() {
     );
     $options[$theme->name] = $theme->info['name'];
 
-    if (drupal_theme_access($theme)) {
-      $form[$theme->name]['operations'] = array('#markup' => l(t('configure'), 'admin/appearance/settings/' . $theme->name) );
-    }
-    else {
-      // Dummy element for drupal_render. Cleaner than adding a check in the theme function.
-      $form[$theme->name]['operations'] = array();
-    }
+    $form[$theme->name]['operations'] = drupal_theme_access($theme) ? array('#type' => 'link', '#title' => t('configure'), '#href' => 'admin/appearance/settings/' . $theme->name) : array();
+
     if (!empty($theme->status)) {
       $status[] = $theme->name;
     }
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.829
diff -u -p -r1.829 system.module
--- modules/system/system.module	30 Oct 2009 22:33:35 -0000	1.829
+++ modules/system/system.module	31 Oct 2009 09:19:51 -0000
@@ -447,7 +447,7 @@ function system_element_info() {
   // Form structure.
   $types['item'] = array(
     '#markup' => '',
-    '#theme' => 'markup',
+    '#pre_render' => array('drupal_pre_render_markup'),
     '#theme_wrappers' => array('form_element'),
   );
   $types['hidden'] = array(
@@ -460,7 +460,11 @@ function system_element_info() {
   );
   $types['markup'] = array(
     '#markup' => '',
-    '#theme' => 'markup',
+    '#pre_render' => array('drupal_pre_render_markup'),
+  );
+  $types['link'] = array(
+    '#pre_render' => array('drupal_pre_render_link', 'drupal_pre_render_markup'),
+    '#leaf' => TRUE,
   );
   $types['fieldset'] = array(
     '#collapsible' => FALSE,
Index: modules/taxonomy/taxonomy.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v
retrieving revision 1.79
diff -u -p -r1.79 taxonomy.admin.inc
--- modules/taxonomy/taxonomy.admin.inc	24 Oct 2009 04:23:51 -0000	1.79
+++ modules/taxonomy/taxonomy.admin.inc	31 Oct 2009 09:19:51 -0000
@@ -20,9 +20,9 @@ function taxonomy_overview_vocabularies(
     $form[$vocabulary->vid]['#vocabulary'] = $vocabulary;
     $form[$vocabulary->vid]['name'] = array('#markup' => check_plain($vocabulary->name));
     $form[$vocabulary->vid]['weight'] = array('#type' => 'weight', '#delta' => 10, '#default_value' => $vocabulary->weight);
-    $form[$vocabulary->vid]['edit'] = array('#markup' => l(t('edit vocabulary'), "admin/structure/taxonomy/$vocabulary->vid"));
-    $form[$vocabulary->vid]['list'] = array('#markup' => l(t('list terms'), "admin/structure/taxonomy/$vocabulary->vid/list"));
-    $form[$vocabulary->vid]['add'] = array('#markup' => l(t('add terms'), "admin/structure/taxonomy/$vocabulary->vid/list/add"));
+    $form[$vocabulary->vid]['edit'] = array('#type' => 'link', '#title' => t('edit vocabulary'), '#href' => "admin/structure/taxonomy/$vocabulary->vid");
+    $form[$vocabulary->vid]['list'] = array('#type' => 'link', '#title' => t('list terms'), '#href' => "admin/structure/taxonomy/$vocabulary->vid/list");
+    $form[$vocabulary->vid]['add'] = array('#type' => 'link', '#title' => t('add terms'), '#href' => "admin/structure/taxonomy/$vocabulary->vid/list/add");
   }
 
   // Only make this form include a submit button and weight if more than one
@@ -354,7 +354,7 @@ function taxonomy_overview_terms($form, 
       unset($form[$key]['#term']['parents'], $term->parents);
     }
 
-    $form[$key]['view'] = array('#markup' => l($term->name, "taxonomy/term/$term->tid"));
+    $form[$key]['view'] = array('#type' => 'link', '#title' => $term->name, '#href' => "taxonomy/term/$term->tid");
     if ($vocabulary->hierarchy < 2 && count($tree) > 1) {
       $form['#parent_fields'] = TRUE;
       $form[$key]['tid'] = array(
@@ -372,7 +372,7 @@ function taxonomy_overview_terms($form, 
         '#default_value' => $term->depth,
       );
     }
-    $form[$key]['edit'] = array('#markup' => l(t('edit'), 'taxonomy/term/' . $term->tid . '/edit', array('query' => drupal_get_destination())));
+    $form[$key]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => 'taxonomy/term/' . $term->tid . '/edit', '#options' => array('query' => drupal_get_destination()));
   }
 
   $form['#total_entries'] = $total_entries;
