common.inc, menu.inc, global - Added a delivery_callback field to {menu_router} table, settable via hook_menu(). The reason for this is that with the code currently in HEAD, the menu item is the primary determinant of whether an HTML page or an AJAX response is wanted. For example, it's assumed that system/ajax, book/js/form, and a few other menu items should deliver an AJAX response and almost everything else should deliver an HTML page, so this provides a way to maintain that convention. Note: to handle a more sophisticated determination for whether an HTML page or an AJAX response is wanted, modules can implement hook_page_delivery_callback_alter(). - Moved everything that index.php does after menu_execute_active_handler() into drupal_deliver_html_page(), since all that code only does what's appropriate when the browser wants an html page. This allows index.php to work as a generic front controller for all requests, without requiring responses other than HTML pages to early exit. - Changed menu_execute_active_handler() to take an extra parameter to indicate whether it should deliver the page callback response to the browser (since index.php no longer does that) or whether it should return the page callback response (so other code within Drupal can re-use it). I struggled with what the default for this parameter should be, and decided to make it such that index.php is kept simple, even though it meant changing all other places that call menu_execute_active_handler() to pass in FALSE. - Added logic to menu_execute_active_handler() to route to the appropriate delivery callback. Seemed to make sense here, since the most likely determinant of which delivery callback to use is part of the router item. To not force this to be the only determinany, added alter() support. Chx recommended to provide alter support for both before and after the page callback runs, since there might be cases where that determination can be made earlier, but there might be special cases where it can only be made later. - Based on feedback from merlinofchaos, changed drupal_site_offline(), drupal_not_found(), and drupal_access_denied() to have a fail-safe if the desired delivery callback isn't an HTML page. ajax.inc: - Added ajax_deliver() - Changed ajax_form_callback() to just return what the callback returns. This makes it so that an AJAX callback can return the exact same thing, regardless whether it is registered via #ajax['path'] or via #ajax['callback']. The reason for the latter is to make the FAPI stuff easier; there's no reason to have different expectations for what is returned. book module - Changed book/js/form to use the ajax_deliver() callback to enable the below use-case. - Changed book_form_update() to return a renderable array. - This demonstrates that this patch enables something that cannot currently be done in HEAD, which is for an AJAX callaback that isn't using 'system/ajax' as its path to be able to return something rather than call ajax_render() and force an early exit. Functions that force an early exit cannot be re-used or unit tested. - Note: since this particular function is just returning content and isn't doing anything fancy AJAX-wise, the return statement could even be simplified to @code return $form['book']['plid'] @endcode and the ajax_command_replace() above it could be removed. However, I chose not to do so in this patch, in order to demonstrate that AJAX callbacks still have a way to return full-blown AJAX commands arrays if they want to. file module - Changed file/ajax and file/progress to use the ajax_deliver() delivery callback. Did not change the implementations of the page callbacks for these items to demonstrate that existing AJAX callbacks can be changed to use the new delivery method without introducing a regression. Also, there are other AJAX callbacks in core whose delivery callbacks are not changed by this patch to demonstrate that this patch doesn't introduce a regression in that regard either. Both of these points are what one would expect, given that all AJAX callbacks currently in HEAD end with an early exit, in which case, we never get to the delivery callback function. My goal once this patch is committed is to submit a follow up patch converting all AJAX callbacks to use ajax_deliver() and not early exit. poll module - Changed poll_choice_js() to demonstrate that with this patch, it's possible (and in my opinion, preferable) to return a renderable array instead of an HTML string. Note: did not similarly change field module's field_add_more_js() in order to demonstrate that it remains possible to return an HTML string. system module - Added a new renderable element type 'ajax_commands'. This allows us to retain the current functionality of AJAX callbacks being able to return complex command arrays. Note, this new element type does not have a default #theme, which means if an AJAX callback returns a command array, but menu_execute_active_handler() decides that an HTML page is what needs to be returned, then no representation of this array gets rendered. I think this is the appropriate behavior for core. However, a module can choose to add a #theme implementation if it does want to implement a representation of an AJAX commands array for display to an HTML page.