Problem/Motivation
Since Drupal 11.4, UrlGenerator::generateFromRoute() passes route, route_name and route_parameters to outbound path processors (#3202329: Outbound path processors miss the route name and parameters). For a URL built with Url::fromRoute('<current>'), which is how core builds every #ajax URL, the route name is the one the caller asked for: <current>. The route object and the parameters are the ones of the page being viewed, because RouteProcessorCurrent copies them from the current route match.
DomainSourcePathProcessor::processOutbound() reads route_name as the route the path belongs to. With <current>:
- the entity lookup is skipped, because it only runs for route names starting with
entity., so the URL is never sent to the entity's source domain; - the excluded route names (
excluded_route_namesandhook_domain_source_excluded_route_names_alter()) and the excluded link templates are checked against<current>, never against the real route.
Example: node 123 has yy.example.com as its source domain and is viewed at /node/123 on xx.example.com. Its language switcher links and pagers are <current> URLs of entity.node.canonical, which is not excluded.
- On 3.x with core 10.2 to 11.3, core passes no route name,
DomainSourceRouteMatchermatches the path toentity.node.canonical, and these links go to yy.example.com. - On 3.x with core 11.4, and on 4.x since #3623227: Drop DomainSourceRouteMatcher and its route provider on 4.x, now that core 11.4 gives the path processor the route removed the matcher, they stay on xx.example.com.
So the same 3.x release behaves differently depending on the core version, and a site upgrading core to 11.4 silently loses the rewrite.
The 2.0.x branch has the opposite symptom, AJAX URLs of edit forms being rewritten despite the edit_form exclusion, and is handled in #3631119: domain_source: AJAX requests on entity edit forms are rewritten to the entity's source domain since Drupal 11.4 (2.0.x).
Steps to reproduce
- Drupal 11.4, domain 3.x or 4.x, Domain Source enabled, two domains xx.example.com and yy.example.com.
- Create a node with yy.example.com as its source domain.
- On xx.example.com, generate
Url::fromRoute('<current>')while viewing the node's canonical page. - The URL stays on xx.example.com. With core 11.3, or with
Url::fromRoute('entity.node.canonical', ['node' => 123]), it is rewritten to yy.example.com.
Proposed resolution
In processOutbound(), when route_name is <current>, replace it with the current route match's route name. The route object and the parameters already describe that route, so the entity lookup and both exclusion checks see the real route again. This reads the route the kernel already matched for the request: no route matching, no query.
Add a kernel test: a <current> URL on entity.node.canonical, for a node whose source domain is not the active one, is rewritten; the same URL on entity.node.edit_form with edit_form excluded is not.
On 4.x and 3.x the class is @internal and autowired, so current_route_match is injected through the constructor. On 3.0.x the class is not internal and its constructor stays as it is, so the current route match is read through a static helper.
Fix on 4.x, then backport to 3.x and 3.0.x.
Remaining tasks
- MR with the fix and the test.
- 3.x and 3.0.x backports.
User interface changes
None.
API changes
None.
Data model changes
None.
AI-Generated: Yes (Claude Code was used to help draft this issue summary. I reviewed it. The analysis was read from the 3.x and 4.x code and from core 11.4's UrlGenerator and RouteProcessorCurrent; nothing was run yet.)
Issue fork domain-3631129
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
mably commentedComment #4
mably commentedComment #5
mably commentedComment #11
mably commented