Skip to content
Anthony Herman
Journal

AGENT SPEC: firefox address-bar grok send

Agent spec. Keep engine 805b8ce4-df7b-4179-a54f-50597d19e8af. Click Send iff webNavigation transitionType is generated or keyword and from_address_bar is set.

agentfirefoxgrok

Audience is an agent. Apply this file. Do not replace it with prose.

Confirmed behavior at addon version 1.0.3: an address-bar query submits and the confirm dialog does not paint. Leave content.js and background.js unchanged unless the user asks for a behavior change.

end state

  • Firefox release address bar and @grok submit to https://grok.com/?q={searchTerms}.
  • Confirm dialog is clicked only for those navigations.
  • Pasted grok.com/?q= URLs, bookmarks, and clicked links keep the dialog. The message is not sent.

click predicate

All of the following. Top frame only (frameId 0).

  • transitionQualifiers contains from_address_bar
  • transitionType is generated or keyword
  • https
  • host is grok.com or www.grok.com
  • q is non-empty
start transitionType from_address_bar click
address-bar search generated yes yes
@grok keyword keyword yes yes
pasted URL typed yes no
bookmark auto_bookmark yes no
clicked link link no no
client_redirect or server_redirect any any keep the tab flag
any other top-level navigation any any clear the tab flag

Tag source: WebNavigation.sys.mjs onURLBarUserStartNavigation. Readable only by the extension on webNavigation.onBeforeNavigate and webNavigation.onCommitted. Page JS cannot read it. Trust the background record. Do not infer the address bar from referrer, Sec-Fetch-Site, or a URL flag.

bindings

firefox.binary: /usr/lib/firefox/firefox
firefox.wrapper: /usr/bin/firefox
firefox.version: 157.0.1
firefox.channel: release
app.id: {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
profile: /home/moji/.mozilla/firefox/xoybst8z.default-release-1710381840470
installs.ini: Install4F96D1932A9F858E
profile.user.js: extensions.startupScanScopes = 4
vendor.js: /usr/lib/firefox/browser/defaults/preferences/vendor.js
vendor.autoDisableScopes: 11
browser.startup.page: 3
MOZ_REQUIRE_SIGNING: true
MOZ_UNSIGNED_APP_SCOPE: true
MOZ_UNSIGNED_SYSTEM_SCOPE: true
MOZ_ALLOW_ADDON_SIDELOAD: true
xpinstall.signatures.required=false: ignored
SCOPE_PROFILE: 1
SCOPE_USER: 2
SCOPE_APPLICATION: 4
SCOPE_SYSTEM: 8
XREAddonAppDir: /usr/lib/firefox/browser
scan_dir: /usr/lib/firefox/browser/extensions
engine.id: 805b8ce4-df7b-4179-a54f-50597d19e8af
engine.name: Grok
engine.template: https://grok.com/?q={searchTerms}
engine.keyword: @grok
engine._loadPath: [user]
engine.defaultEngineId: 805b8ce4-df7b-4179-a54f-50597d19e8af
engine.privateDefaultEngineId: 805b8ce4-df7b-4179-a54f-50597d19e8af
engine.appDefault: google
search.file: <profile>/search.json.mozlz4
search.backup: <profile>/search.json.mozlz4.bak-before-grok
search.format: mozLz40 NUL + uint32le uncompressed size + raw LZ4
addon.id: grok-address-bar-send@moji.local
addon.version: 1.0.3
addon.source: /home/moji/.local/share/grok-address-bar-send
addon.pointer: /usr/lib/firefox/browser/extensions/grok-address-bar-send@moji.local
addon.pointer.bytes: /home/moji/.local/share/grok-address-bar-send\n
addon.pointer.mode: regular file 644 root. not a symlink
addon.location: app-global
addon.permissions: [webNavigation]
addon.origins: [https://grok.com/*, https://www.grok.com/*]
content_scripts.run_at: document_start
event: grok-address-bar-submit
event.detail: { source: "firefox-address-bar", transition }
messages: grok-bar-check, grok-bar-done
dialog.selector: [data-analytics-name="link-query-confirm-dialog"]
dialog.heading: /send this message/i
send.control: /^send$/i on trimmed text or aria-label
dialog.wait_ms: 20000
dialog.poll_ms: 100
dialog.reclick_ms: 400
check.retries_when_q: 8
check.gap_ms: 40
check.retries_without_q: 1
private_browsing: absent. no internal:privateBrowsingAllowed
stale_pointer_not_scanned: /usr/lib/firefox/extensions/grok-address-bar-send@moji.local

invariants

  • Do not put the query text in the event, a hash, or a query flag.
  • grok.com does not listen for grok-address-bar-submit. The click sends.
  • Click only the enabled Send control inside the confirm dialog. Walk open shadow roots. Call button.wrappedJSObject.click().
  • Do not click button[aria-label="Submit"] or [data-testid="chat-submit"].
  • Success is the dialog node absent. A returned function is not success.
  • Second click only if the dialog is still present 400ms later.
  • On timeout, disconnect the observer and do not send grok-bar-done.
  • Send grok-bar-done only after the dialog is gone, then clear the tab flag. Also clear on tabs.onRemoved.
  • A thrown sendMessage is a retry.
  • Dispatch the event with window.wrappedJSObject and cloneInto. An inline script is blocked by the grok.com CSP nonce.
  • Do not reimplement the conversation API. The Send onClick submits with linkQuery: true, disableMemory: true, disablePersonalization: true. It ignores the event argument. isTrusted is not checked.
  • Do not stop React from opening the dialog. Click in the same animation frame the node is inserted.
  • Logged-in grok.com opens the dialog only after subscriptions leave pending. Cover that delay. Anonymous sessions are not delayed by that check.
  • Do not rewrite search.json.mozlz4 while both engine ids equal engine.id. A bad verification hash resets the default to appDefault google. Leave search.json.mozlz4.bak-before-grok in place.
  • Do not install under the profile extensions/ directory, ~/.mozilla/extensions/, /usr/lib/firefox/extensions, or /usr/lib/mozilla/extensions/{app.id}/.
  • Do not grant <all_urls>.
  • Do not remove extensions.startupScanScopes 4 from profile user.js.
  • Do not edit prefs.js or extension-preferences.json while Firefox is running.
  • Do not leave remote debugging or MOZ_AUTOMATION on.
  • Do not edit extensions.json until an addon row exists.
  • browser.startup.page is 3. Restart Firefox only when a code or permission change must load. One process. Do not use this profile for experiments.
  • Do not use a pasted ?q= load as proof the click works.
  • Private windows do not run this addon until the user enables Run in Private Windows. Do not add a policies.json for that unless the user asks. A managed-browser banner is the result.
  • The user.js comment names /usr/lib/firefox/extensions. Ignore the comment. The pref value 4 is the scanned scope. The scanned directory is scan_dir.
  • The stale pointer is inert. Do not add a second live copy. Deleting it needs root. Not required.
  • Bump manifest.json version when JS changes. Sideload does not hot-reload.

background

Map tabId to { transition, at }.

On onBeforeNavigate and onCommitted, frameId 0:

  • predicate matches: set the tab
  • else qualifiers contain client_redirect or server_redirect: keep
  • else: delete

grok-bar-check returns { addressBar: true, transition } or { addressBar: false }. grok-bar-done deletes the tab.

content

Run at document_start.

  1. If the document URL has q, call grok-bar-check up to 8 times, 40ms apart. Otherwise once. A throw sets the answer null and continues.
  2. If addressBar is false, return.
  3. Dispatch grok-address-bar-submit on the page window. Detail is event.detail only.
  4. MutationObserver on document.documentElement: childList, subtree, attributes data-analytics-name, data-state, role, disabled. On mutation, requestAnimationFrame then tryClick.
  5. setInterval tryClick every 100ms. setTimeout 20000ms stops the observer and the interval.
  6. tryClick: if a click already happened and the dialog is gone, send grok-bar-done and stop. If a click happened less than 400ms ago, return. Otherwise click Send.
  7. Find the dialog: dialog.selector, else [role=dialog], [role=alertdialog], or [aria-modal=true] whose text matches dialog.heading, else a heading matching that pattern and its closest dialog or parent.
  8. Send control: button or [role=button] under that root, including shadow roots, not disabled, text or aria-label matching send.control.

install

Source directory is user-owned, mode 644: manifest.json, background.js, content.js. Gecko id equals addon.id and the pointer filename. strict_min_version 128.0. gIDTest accepts this id.

Pointer file leaf name is addon.id. First line is the absolute source path plus newline.

4 & autoDisableScopes 11 == 0, so a new application-scope addon starts enabled. Unsigned application scope is SIGNEDSTATE_NOT_REQUIRED.

extensions.startupScanScopes defaults to 0 when extensions.lastAppBuildId is unchanged. Empty locations are omitted from addonStartup.json.lz4. The user.js pref is what makes the next source mtime scan.

MV3 host_permissions are optional origins and are not auto-granted on a future reinstall. The live grant is profile extension-preferences.json for addon.id:

{"permissions":[],"origins":["https://grok.com/*","https://www.grok.com/*"],"data_collection":[]}

If those origins are missing, the content script does not run. Re-grant those two origins only.

reload

Quit with SIGTERM on the single /usr/lib/firefox/firefox pid. The parent is systemd --user. Plasma does not relaunch it.

Restart with setsid. Copy DISPLAY, XAUTHORITY, XDG_RUNTIME_DIR, DBUS_SESSION_BUS_ADDRESS, and DESKTOP_SESSION from the old process environ. Do not start a second instance.

Then require all of:

  • one firefox pid
  • extensions.json: this id, active true, userDisabled false, appDisabled false, location app-global, version equals the manifest
  • addonStartup.json.lz4: app-global path scan_dir, addon enabled
  • extension-preferences.json origins equal addon.origins
  • both engine ids still engine.id

verify

  1. Address-bar Enter reaches Grok and the confirm dialog does not remain.
  2. A bookmark or a pasted https://grok.com/?q=test still shows the dialog and does not send.
  3. Both engine ids are unchanged.
  4. One firefox pid.

reject

  • Profile or user scope. Release requires a Mozilla signature. xpinstall.signatures.required=false does nothing.
  • System scope /usr/lib/mozilla/extensions/{app.id}/. 8 & 11 != 0, so it installs disabled.
  • /usr/lib/firefox/extensions. This binary does not scan it. KEY_APP_GLOBAL is scan_dir.
  • Headless without -no-remote attaches to the running browser and exits. Do not use it as an install test on this profile.
  • Treating a page React onClick call as success. The watcher stops while the dialog is still open.
  • Aborting the address-bar check on the first sendMessage throw.
  • A #gwc= token or any other page-side secret. Any link can forge it.
  • Clicking Send for every ?q= load.

Fail Forward.