Chrome: browser rules stop matching in windows that have shown a New Tab Page (`chrome://newtab-footer/` WebArea is picked as the tab URL)

Abierto Apto para principiantes
#153 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
72/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
macos, swift
Área
desktop, devtools

Línea de trabajo

Empieza en Utilities/Accessibility/QueryWebAreaService.swift, en fetchScrollArea(), donde la DFS devuelve el primer AXWebArea cuyo esquema no es chrome-extension. En las ventanas que ya han mostrado una NTP, el WebArea del pie de página con chrome://newtab-footer/ se extrae primero. Ejecuta el fragmento dump.swift del issue en una ventana afectada para confirmar la URL elegida. Se considera terminado cuando se omite el WebArea del pie de página (por ejemplo, excluyendo también el esquema chrome, o haciendo coincidir el WebArea cuya URL es igual a AXDocument) y una pestaña de github.com se resuelve a su propia URL en una ventana que ha mostrado la NTP.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Summary

Current Chrome keeps a hidden NewTabFooterWebView (chrome://newtab-footer/, the NTP footer showing "Managed by your organization" / extension attribution) in every browser window that has displayed a New Tab Page. It stays in the window's accessibility tree for the lifetime of the window, as a sibling of the active tab's AXWebArea.

QueryWebAreaService.fetchScrollArea() returns the first AXWebArea its DFS reaches and only filters the chrome-extension scheme. In such windows the footer WebArea is reached first, so the current URL resolves to chrome://newtab-footer/, no browser rule matches, and the window keeps whatever input source was active (reason=noChanges). Which site is open in the window makes no difference.

Managed/enterprise Chrome shows the footer by default, so this hits every such user after a single ⌘T in a window. Related observation in a comment on #42.

Environment
  • Input Source Pro 2.14.0 (build 1182)
  • macOS 26.7.1 (25G241)
  • Google Chrome 157.0.8103.26, enterprise-managed (NTP footer visible)
  • Chrome URL switching enabled, Accessibility granted
  • Rule: DOMAIN example.com → Squirrel (Rime)
Steps to reproduce

The NTP footer must be visible. On an unmanaged Mac, either of these makes Chrome show it:

  • Extension attribution: install any extension that overrides the New Tab Page (e.g. "Speed Dial"). The NTP then shows " has changed this page" in the footer.

  • Management notice: apply any harmless platform policy, which marks the browser as managed:

    defaults write com.google.Chrome ShowHomeButton -bool true
    # restart Chrome; chrome://policy lists it and the NTP footer shows "Managed by your organization"
    # cleanup afterwards: defaults delete com.google.Chrome ShowHomeButton
    

Then:

  1. Add a browser rule, e.g. DOMAIN github.com → <any CJK input source>; enable URL switching for Chrome.
  2. In a Chrome window, press ⌘T once (the footer is visible on the new tab), then open github.com in that window.
  3. Switch to another app and back to the Chrome window.

Expected: the rule fires and the input source switches.
Actual: no switch. The diagnostics log shows state.published … reason=noChanges pid=<chrome> and no switch.* entry.

  1. Drag the same tab into a new window and repeat step 3: the rule fires (switch.target … reason=appSpecified).

Closing the tab and restoring it with ⌘⇧T keeps the problem; the footer WebView belongs to the window (BrowserView), and only recreating the window removes it.

To inspect what fetchScrollArea() sees, run this from a terminal with Accessibility permission while the affected window is focused (swift -module-cache-path /tmp/swift-cache dump.swift):

import ApplicationServices
import AppKit
let chrome = NSRunningApplication.runningApplications(withBundleIdentifier: "com.google.Chrome").first!
let app = AXUIElementCreateApplication(chrome.processIdentifier)
func attr(_ e: AXUIElement, _ a: String) -> AnyObject? { var v: AnyObject?; AXUIElementCopyAttributeValue(e, a as CFString, &v); return v }
func str(_ e: AXUIElement, _ a: String) -> String { if let u = attr(e, a) as? URL { return u.absoluteString }; return (attr(e, a) as? String) ?? "" }
let win = attr(app, kAXFocusedWindowAttribute) as! AXUIElement
print("AXDocument =", str(win, kAXDocumentAttribute))
var stack = [win]
while let el = stack.popLast() {                       // same traversal order as fetchScrollArea()
    let role = str(el, kAXRoleAttribute)
    if role == "AXWebArea" { print("AXWebArea url =", str(el, kAXURLAttribute)); continue }
    let kids = (role == "AXTable" || role == "AXOutline")
        ? (attr(el, kAXVisibleRowsAttribute) as? [AXUIElement]) ?? []
        : (attr(el, kAXChildrenAttribute) as? [AXUIElement]) ?? []
    stack.append(contentsOf: kids)
}

The first printed AXWebArea is the one Input Source Pro uses. In an affected window it is chrome://newtab-footer/.

Evidence

Replicating fetchScrollArea() (DFS with popLast, same filter) over the AX tree of each Chrome window:

=== window[0] main=1  title=<page on github.com>
    AXDocument=https://github.com/...
    webArea#1 url=chrome://newtab-footer/      <== picked
        path=AXWindow > AXGroup[0/4] > AXGroup[0/1] > AXGroup[0/1] > AXGroup[0/1] > AXGroup[1/4] > AXGroup[0/2] > AXScrollArea[2/3] > AXWebArea[0/1]
    webArea#2 url=https://github.com/...
        path=AXWindow > AXGroup[0/4] > AXGroup[0/1] > AXGroup[0/1] > AXGroup[0/1] > AXGroup[1/4] > AXGroup[0/2] > AXScrollArea[1/3] > AXWebArea[0/1]

=== window[1] main=0  title=<page on github.com, window never showed an NTP>
    AXDocument=https://github.com/...
    webArea#1 url=https://github.com/...        <== picked
        path=AXWindow > ... > AXGroup[0/2] > AXScrollArea[1/2] > AXWebArea[0/1]

Windows with the footer have three AXScrollArea children; the footer is the last one, so the DFS pops it first. The window's AXDocument is correct in both cases.

Diagnostics log (IndicatorDiagnosticsEnabled), affected window:

state.published source=com.apple.keylayout.US reason=noChanges pid=1099
activation.mode mode=autoShow app=com.google.Chrome pid=1099

Same tab after moving it to a fresh window:

switch.target source=im.rime.inputmethod.Squirrel.Hans mode=im.rime.inputmethod.Squirrel.Hans strategy=nil
switch.TISSelect source=im.rime.inputmethod.Squirrel.Hans reason=target status=0
state.published source=im.rime.inputmethod.Squirrel.Hans::im.rime.inputmethod.Squirrel.Hans reason=appSpecified pid=1099
Code path
  • Browser.getCurrentTabURL() (Utilities/BrowserURL.swift) uses QueryWebAreaService.perform() for Chromium browsers.

  • QueryWebAreaService.fetchScrollArea() (Utilities/Accessibility/QueryWebAreaService.swift):

    if element.role == "AXWebArea",
       element.url?.scheme != "chrome-extension"
    {
        return element
    }
    
  • normalizedTabURL() maps chrome://newtab/ and chrome://new-tab-page/ to .newtab; chrome://newtab-footer/ passes through unchanged into getBrowserRule(url:).

Suggested fix

Minimal:

if element.role == "AXWebArea",
   let scheme = element.url?.scheme,
   scheme != "chrome-extension",
   scheme != "chrome"   // chrome://newtab-footer/ and other internal WebUI panes
{
    return element
}

More robust: when the window exposes an AXDocument URL, return the WebArea whose URL equals it (the Comet/Aside path already trusts documentURL), and fall back to the DFS result otherwise. This also covers future hidden WebUI panes such as side panels.

Workaround

New Tab Page → right-click the footer → "Hide footer on new tab page", then close and reopen the affected Chrome windows (per profile).

I can send a PR for either variant.

Lenguaje dominante
Swift
Estrellas
3.6k
Forks
144
Merge medio
4 h 16 min
PR fusionados (30 d)
3

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de runjuu/InputSourcePro

Todos los issues de runjuu/InputSourcePro

Issues similares

Más issues de Swift

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.