Class BrowserBoot
Browser through its load + readiness + retry lifecycle.The SWT Browser widget on the Edge/WebView2 backend (Windows 11) does not finish initializing until its control hierarchy is actually visible on screen. A setUrl (or setText) issued before that moment can be silently dropped: the page never loads, the injected BrowserFunction bridge never becomes live, and any JavaScript→Java readiness callback the site waits for never fires. The browser cell sits blank. Re-issuing the navigation (a manual "Reload") always fixes it, because by then the control is visible.
BrowserBoot centralizes that lifecycle: it owns the readiness handshake, the gated/retrying load, the file: URL pre-validation, and (via the create(org.eclipse.swt.widgets.Composite) factory) creation-failure handling. The site continues to register its own other BrowserFunctions directly on the browser; BrowserBoot does not wrap or manage those.
Two distinct readiness moments are exposed. Each may be supplied either as a Consumer<Browser> (browser only) or as a BiConsumer<Browser,Object[]> (browser plus the readiness call's JavaScript arguments); both forms are nullable:
onBridge— fires when the readinessBrowserFunctionis called from the page (JS→Java confirmed). Most sites passnullhere.onReady— by default fires once, on the UI thread, when the browser is REALLY ready, meaning BOTH the readinessBrowserFunctionhas fired (JS→Java works) ANDProgressListener.completed(org.eclipse.swt.browser.ProgressEvent)has fired (Java→JS into the loaded page works). In refire mode it fires on each subsequent bridge call too (see below).
The readiness signal reaches BrowserBoot one of two ways. Either BrowserBoot owns the BrowserFunction (register it with readyFunction(java.lang.String)), or the site owns a BrowserFunction of its own and calls markBridgeFired() from inside it. The latter is required when the site's readiness function is overloaded — called both as the bare handshake and, separately, to return data (e.g. a computed size) — because a site-owned function can demultiplex by argument count whereas a BrowserBoot-owned one cannot. When the BiConsumer forms are used, the arguments from the bridge call are stashed and replayed into onReady, so onReady always sees the bridge's arguments regardless of whether the bridge or completed fired last.
REFIRE MODE (opt-in, enableReadyRefire(boolean)): by default onReady fires exactly once. Refire mode makes it fire on each subsequent readiness-bridge call instead, for a STABLE page whose readiness function is deliberately called more than once (e.g. an overloaded onJSInitialized that returns a computed size on a later call). The mode imposes a page contract: the page MUST NOT be re-loaded (a second load(java.io.File)/loadText(java.lang.String) throws IllegalStateException while refire mode is on) and MUST NOT navigate (a real in-page navigation automatically disables refire mode, resets the readiness state, and degrades the instance to fire-once). A completed event alone never re-fires onReady; only a bridge call does.
CRITICAL: the retry (re-issuing the load) is gated on the readiness BrowserFunction having fired ONLY, never on completed. The original hang was the SWT BrowserFunctions never being injected into the page at all; in that hang completed can still fire (the document loaded) while the bridge is dead, so gating retry on completed would fail to retry the actual hang. The BrowserFunction firing proves injection succeeded, which is the correct retry-stop signal.
All callbacks run on the SWT UI thread (BrowserFunction callbacks and timerExec are UI-thread by SWT contract), so the per-instance state needs no synchronization.
- Author:
- Christopher Mindus
Method Summary
Modifier and TypeMethodDescriptionstatic BrowserBootWraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g.static BrowserBootattach(Browser browser, BiConsumer<Browser, Object[]> onBridgeParams, BiConsumer<Browser, Object[]> onReadyParams) Wraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g.static BrowserBootWraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g.static BrowserBootCreates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.static BrowserBootCreates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.static BrowserBootcreate(Composite parent, Color background, BiConsumer<Browser, Object[]> onBridgeParams, BiConsumer<Browser, Object[]> onReadyParams) Creates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.static BrowserBootCreates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.enableReadyRefire(boolean on) Enables or disables REFIRE MODE.Returns theBrowser, or null if creation failed.Returns what is actually in the layout: theBrowser, or the fallback error Composite.booleanReturns whether a realBrowserwas created.voidIssues a gated, validated, retryingsetUrl.voidIssues a gated, validated, retryingsetUrl.voidIssues a gated, validated, retryingsetUrl.voidIssues a gated, retryingsetText.voidloadWebRoot(String path) This method loads a file relative the web server document root.voidMarks the JS→Java bridge as fired from outside, for sites whose readiness signal is a site-ownedBrowserFunctionrather than a BrowserBoot-registeredreadyFunction(java.lang.String).voidmarkBridgeFired(Object[] params) Marks the JS→Java bridge as fired from outside, carrying the arguments the site-ownedBrowserFunctionreceived.onBridge(BiConsumer<Browser, Object[]> onBridgeParams) Sets theonBridgecallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)).Sets theonBridgecallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)).onReady(BiConsumer<Browser, Object[]> onReadyParams) Sets theonReadycallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)).Sets theonReadycallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)).readyFunction(String name) Registers the readinessBrowserFunction— the JS→Java half of the handshake.booleanRuns a script in the SWTBrowserusingbrowser.evaluate(script).booleanSets the browser's background color on the HTML body.booleanSets the browser's foreground (text) color on the HTML body.
Method Details
create
Creates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.The factory ALWAYS returns a
BrowserBoot(never null). When browser creation fails (e.g. Windows GDI/USER handle exhaustion) the wrapped component is a read-only fallback Composite showing the exception, andisBrowser()returns false. The caller lays outgetComponent()(the browser OR the error UI) and only callsload(java.io.File)whenisBrowser()is true.- Parameters:
parent- The parent composite.- Returns:
- A new
BrowserBoot, never null.
create
Creates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.The factory ALWAYS returns a
BrowserBoot(never null). When browser creation fails (e.g. Windows GDI/USER handle exhaustion) the wrapped component is a read-only fallback Composite showing the exception, andisBrowser()returns false. The caller lays outgetComponent()(the browser OR the error UI) and only callsload(java.io.File)whenisBrowser()is true.- Parameters:
parent- The parent composite.background- The background color, nullable.- Returns:
- A new
BrowserBoot, never null.
create
public static BrowserBoot create(Composite parent, Color background, Consumer<Browser> onBridge, Consumer<Browser> onReady) Creates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.The factory ALWAYS returns a
BrowserBoot(never null). When browser creation fails (e.g. Windows GDI/USER handle exhaustion) the wrapped component is a read-only fallback Composite showing the exception, andisBrowser()returns false. The caller lays outgetComponent()(the browser OR the error UI) and only callsload(java.io.File)whenisBrowser()is true.onReadyonly ever fires when there IS a browser.- Parameters:
parent- The parent composite.background- The background color, nullable.onBridge- Fires when the readiness function fires; nullable.onReady- Fires once when really ready; nullable.- Returns:
- A new
BrowserBoot, never null.
create
public static BrowserBoot create(Composite parent, Color background, BiConsumer<Browser, Object[]> onBridgeParams, BiConsumer<Browser, Object[]> onReadyParams) Creates a browser viaSWTHelper.createBrowserOrCompositeForFailure(org.eclipse.swt.widgets.Composite, org.eclipse.swt.graphics.Color)and wraps it.The factory ALWAYS returns a
BrowserBoot(never null). When browser creation fails (e.g. Windows GDI/USER handle exhaustion) the wrapped component is a read-only fallback Composite showing the exception, andisBrowser()returns false. The caller lays outgetComponent()(the browser OR the error UI) and only callsload(java.io.File)whenisBrowser()is true.onReadyonly ever fires when there IS a browser.- Parameters:
parent- The parent composite.background- The background color, nullable.onBridgeParams- Fires when the readiness function fires, with that call's arguments; nullable.onReadyParams- Fires once when really ready, with the bridge call's arguments; nullable.- Returns:
- A new
BrowserBoot, never null.
attach
Wraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g. the tooltip's measure-then-show flow), with no callbacks set.This is the no-callback overload, for sites that drive readiness entirely through a site-owned
BrowserFunction(gating the retry withmarkBridgeFired()) and therefore need neitheronBridgenoronReady. It exists to avoid an ambiguous(null,null)call between theConsumerandBiConsumeroverloads, and the casts that would otherwise be required. Callbacks may still be set afterwards viaonBridge/onReady.- Parameters:
browser- The browser to drive; not null.- Returns:
- A new
BrowserBoot, never null.
attach
public static BrowserBoot attach(Browser browser, Consumer<Browser> onBridge, Consumer<Browser> onReady) Wraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g. the tooltip's measure-then-show flow).- Parameters:
browser- The browser to drive; not null.onBridge- Fires when the readiness function fires; nullable.onReady- Fires once when really ready; nullable.- Returns:
- A new
BrowserBoot, never null.
attach
public static BrowserBoot attach(Browser browser, BiConsumer<Browser, Object[]> onBridgeParams, BiConsumer<Browser, Object[]> onReadyParams) Wraps an already-createdBrowser(escape hatch for sites that create and manage their own browser, e.g. the tooltip's measure-then-show flow).- Parameters:
browser- The browser to drive; not null.onBridgeParams- Fires when the readiness function fires, with that call's arguments; nullable.onReadyParams- Fires once when really ready, with the bridge call's arguments; nullable.- Returns:
- A new
BrowserBoot, never null.
isBrowser
public boolean isBrowser()Returns whether a realBrowserwas created.- Returns:
- true if a Browser is present, false if creation failed (fallback Composite).
getBrowser
Returns theBrowser, or null if creation failed.- Returns:
- The Browser, or null.
getComponent
Returns what is actually in the layout: theBrowser, or the fallback error Composite. Never null. The caller sets layout data on THIS.- Returns:
- The component to lay out, never null.
readyFunction
Registers the readinessBrowserFunction— the JS→Java half of the handshake.When the page calls this function (from its DOM-ready/init handler),
BrowserBootmarks the bridge as fired, invokesonBridge(if non-null) and, ifcompletedhas also fired, latchesreadyand runsonReady(once, or repeatedly in refire mode; seeenableReadyRefire(boolean)). The name varies by site — pass the exact string the page JS calls (e.g."onJSInitialized").If browser creation failed (no browser), this is a silent no-op: creation failure is a designed, queryable state (see
isBrowser()), not an error. A disposed browser or a second registration are programming errors and throwIllegalStateException.The readiness function is registered ONCE for the lifetime of this
BrowserBoot; a second call always throws. Sites whose readiness function must be site-owned (e.g. an overloaded function that also returns data) do not call this at all — they callmarkBridgeFired()instead.- Parameters:
name- The readiness function name as called by the page.- Returns:
- This
BrowserBoot, for chaining. - Throws:
IllegalStateException- if the browser is disposed, or the readiness function has already been registered.
onBridge
Sets theonBridgecallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)). Any previousonBridgesettings are removed in this call.- Parameters:
onBridge- Fires when the readiness function fires; nullable.- Returns:
- This
BrowserBoot, for chaining.
onBridge
Sets theonBridgecallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)). Any previousonBridgesettings are removed in this call.- Parameters:
onBridgeParams- Fires when the readiness function fires, with that call's arguments; nullable.- Returns:
- This
BrowserBoot, for chaining.
onReady
Sets theonReadycallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)). Any previousonReadysettings are removed in this call.- Parameters:
onReady- Fires when really ready (once by default; repeatedly in refire mode); nullable.- Returns:
- This
BrowserBoot, for chaining.
onReady
Sets theonReadycallback (alternative to passing it tocreate(org.eclipse.swt.widgets.Composite)/attach(org.eclipse.swt.browser.Browser)). Any previousonReadysettings are removed in this call.- Parameters:
onReadyParams- Fires when really ready (once by default; repeatedly in refire mode), with the bridge call's arguments; nullable.- Returns:
- This
BrowserBoot, for chaining.
enableReadyRefire
Enables or disables REFIRE MODE.By default
onReadyfires exactly once, when the browser is really ready. In refire mode it instead fires on EACH subsequent readiness-bridge call (a repeat call to thereadyFunction(java.lang.String), or a repeatmarkBridgeFired()), carrying that call's arguments. This is for a STABLE page whose readiness function is called repeatedly by design — e.g. an overloadedonJSInitializedthat signals init once and is then called again to return a computed size. Acompletedevent alone never re-firesonReady; only a bridge call does.Refire mode imposes a contract on the page (see the class documentation):
- The page MUST NOT be re-loaded: while refire mode is on, a second
load(java.io.File)/loadText(java.lang.String)throwsIllegalStateException. - The page MUST NOT navigate: a real in-page navigation automatically turns refire mode OFF, resets the readiness state, and the instance degrades to fire-once.
The flag is wiring, not per-cycle state: it is not cleared by a (legal) load. It may be toggled at any time.
- Parameters:
on- true to enable refire mode, false to restore fire-once behavior.- Returns:
- This
BrowserBoot, for chaining.
- The page MUST NOT be re-loaded: while refire mode is on, a second
markBridgeFired
public void markBridgeFired()Marks the JS→Java bridge as fired from outside, for sites whose readiness signal is a site-ownedBrowserFunctionrather than a BrowserBoot-registeredreadyFunction(java.lang.String).This is the alternative to
readyFunction(java.lang.String)for sites that, for whatever reason, keep ownership of the readinessBrowserFunctionthemselves (e.g. to demultiplex an overloaded function by argument count entirely in their own code). The site calls this from inside its own function to gate the retry: it setsbridgeFired, invokesonBridge(if non-null) and, ifcompletedhas also fired, latchesreadyand runsonReady(once, or repeatedly in refire mode) — exactly as a registered readiness function would.NOTE: an overloaded readiness function (called once bare for init, then again with data such as a computed size) does NOT require this method. It can be registered normally with
readyFunction(java.lang.String)and the repeat calls delivered throughrefire mode; the font-preview tooltips do exactly that.markBridgeFiredis only needed when the site must own theBrowserFunctionfor some other reason.This no-argument form passes
nullto anyBiConsumercallbacks. To forward the site function's own arguments, usemarkBridgeFired(Object[]). Must be called on the UI thread.markBridgeFired
Marks the JS→Java bridge as fired from outside, carrying the arguments the site-ownedBrowserFunctionreceived. Same asmarkBridgeFired()but the suppliedparamsare passed toonBridge/onReadyBiConsumer callbacks and stashed for theonReadyreplay. Must be called on the UI thread.- Parameters:
params- The site function's arguments, nullable.
loadWebRoot
This method loads a file relative the web server document root. Issues a gated, validated, retryingsetUrl.If the file does not exist, an InternalError is thrown. No-op if there is no browser.
- Parameters:
path- The file path with '/' as directory separators, a file relative the web server root.- Throws:
InternalError- if the file or the web server root cannot be found.IllegalStateException- if refire mode is on and a load has already been issued (the single-load contract; seeenableReadyRefire(boolean)).
load
Issues a gated, validated, retryingsetUrl.The file URL the target is pre-validated: if the file is confirmed missing/unreadable, a clear error is logged and the retry loop is NOT entered (retrying a non-existent file is pointless). If the URL cannot be parsed to a file, the pre-check fails open and the load proceeds. Non-file schemes (
http/https/about/data) are never pre-checked — a transient (e.g. a still-starting localhost server) is exactly what the retry is for. No-op if there is no browser.- Parameters:
file- The file to load.- Throws:
IllegalStateException- if refire mode is on and a load has already been issued (the single-load contract; seeenableReadyRefire(boolean)).MalformedURLException- if the File cannot be converted to an URL due to malformed.toURL()called onfile.toURI().
load
Issues a gated, validated, retryingsetUrl.For a
file:URL the target is pre-validated: if the file is confirmed missing/unreadable, a clear error is logged and the retry loop is NOT entered (retrying a non-existent file is pointless). If the URL cannot be parsed to a file, the pre-check fails open and the load proceeds. Non-file schemes (http/https/about/data) are never pre-checked — a transient (e.g. a still-starting localhost server) is exactly what the retry is for. No-op if there is no browser.- Parameters:
url- The URL to load.- Throws:
IllegalStateException- if refire mode is on and a load has already been issued (the single-load contract; seeenableReadyRefire(boolean)).
load
Issues a gated, validated, retryingsetUrl.For a
file:URL the target is pre-validated: if the file is confirmed missing/unreadable, a clear error is logged and the retry loop is NOT entered (retrying a non-existent file is pointless). If the URL cannot be parsed to a file, the pre-check fails open and the load proceeds. Non-file schemes (http/https/about/data) are never pre-checked — a transient (e.g. a still-starting localhost server) is exactly what the retry is for. No-op if there is no browser.- Parameters:
url- The URL to load.- Throws:
IllegalStateException- if refire mode is on and a load has already been issued (the single-load contract; seeenableReadyRefire(boolean)).
loadText
Issues a gated, retryingsetText. Same asload(java.io.File)but with HTML and no file pre-check. No-op if there is no browser.- Parameters:
html- The HTML to load.- Throws:
IllegalStateException- if refire mode is on and a load has already been issued (the single-load contract; seeenableReadyRefire(boolean)).
setBackgroundColor
Sets the browser's background color on the HTML body. A null color is a no-op. In case of a browser execution error, a severe event is logged (seerunJS(java.lang.String)).- Parameters:
c- The color, ornullfor no operation.- Returns:
- true on success or when
cis null (no-op); false if the script failed.
setForegroundColor
Sets the browser's foreground (text) color on the HTML body. A null color is a no-op. In case of a browser execution error, a severe event is logged (seerunJS(java.lang.String)).- Parameters:
c- The color, ornullfor no operation.- Returns:
- true on success or when
cis null (no-op); false if the script failed.
runJS
Runs a script in the SWTBrowserusingbrowser.evaluate(script). In case of a browser execution error, a severe event is logged andfalseis returned.evaluateis used rather thanexecutebecause it is what most of IIZI uses, and because it raises aSWTExceptionon a script error (which we catch and log) instead of silently returning a flag. NOTE: this only catches errors raised synchronously by the script; an exception thrown later inside an asynchronous callback the script schedules (e.g. asetTimeout) cannot be observed here.- Parameters:
script- The JavaScript string.- Returns:
- true on success; false if there is no (live) browser, or the script raised an error.