{"name":"CT WEBBROWSER","title":"CT WEBBROWSER","type":"topic","url":"https://community.bistudio.com/wiki/CT_WEBBROWSER","revid":379123,"updated":"2026-10-09T12:45:41Z","aliases":[],"markdown":"Arma 3 v2.20\n\n### Introduction\n\nWeb browser control features an embedded Chromium window.\nThe URL is subject to `allowedHTMLLoadURIs[]` whitelisting in Description.ext - CfgCommands.\nctrlSetURL can be used to change the URL after creation of the control.\n\n#### Related commands & functions\n\n- Commands: General\n- Commands: UI Eventhandlers\n\n#### Related User Interface Eventhandlers\n\n- Events: General\n\n### Alphabetical Order\n\n> [!NOTE]\n> TokenNames common to most controls, such as x, y, w, h, text, idc... can be found **here**.\n\n> [!WARNING]\n> Not all of the listed attributes might have an effect nor might the list be complete. All attributes were gathered with [this](https://github.com/7erra/BIKI.VR/blob/master/fnc/generateCTPage.sqf) config crawler.\n\n```cpp\n#define CT_WEBBROWSER 106\n```\n\n#### A\n\n##### allowExternalURL\n\n**Type:** Boolean\n\n**Description:** Whether this browser will allow external URLs, as opposed to local content.\n\n> [!WARNING]\n> This is an experimental feature currently available for testing on Development Branch.\n> It is not yet decided whether this will ever become available in Stable branch.\n\n```cpp\nallowExternalURL = 1;\n```\n\n#### U\n\n##### url\n\n**Type:** String\n\n**Description:** An external (if allowExternalURL is enabled) URL to be opened, or a path to a local file to load. Local files are subject to `allowedHTMLLoadURIs[]`.\n\n```cpp\nurl = \"https://arma3.com\";\n```\n\n### Scripting\n\n> [!NOTE]\n> See ctrlWebBrowserAction.\n\n#### Control Event Handlers\n\nThe following event handlers can only be added to CT_WEBBROWSER controls.\n\n##### JSDialog\n\n- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when JavaScript triggers an `alert()` or `confirm()` dialog.\n\n```sqf\nparams [\"_control\", \"_isConfirmDialog\", \"_message\"];\n```\n\nThe code handling this event can return a Boolean true/false. Which will stop the next Eventhandlers from being triggered, and will be interpreted as a reply to the dialog.\nIf the code does not immediately provide a reply, it should return Nothing.\nIf the code does not return bool, then the script must call ctrlWebBrowserAction with the \"JSDialog\" action to reply to the dialog, the WebBrowser will be unresponsive until a reply has been sent.\n\n##### Draw\n\n- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when the WebBrowser has drawn a new frame (NOT when the game control displays a new frame)\n\n```sqf\nparams [\"_control\"];\n```\n\n##### PageLoaded\n\n- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when the current page has finished loading. This only contains the main page, the page may contain content (scripts, images) that will load asynchronously and may not be ready. This fires multiple times, when the current page (URL) was changed or when the browser is Resumed.\n\n```sqf\nparams [\"_control\"];\n```\n\nOnce PageLoaded is triggered, it is safe to execute JavaScript on the page. Before PageLoaded, the page might not be ready to receive JavaScript and may drop ExecJS requests.\n\n### Default Classes\n\n> [!NOTE]\n> **Arma 3:** **AddOns:** Classes need to be initialised first with `class SomeClass;`\n>\n> ---\n>\n> **Missions:** Since Arma 3 v2.02 one can use `import SomeClass;` to initialise a class (see the import keyword).\n>\n> In older versions, use `\"Default\" call BIS_fnc_exportGUIBaseClasses;` and paste the result into the description.ext.\n\n#### RscWebBrowser\n\nBaseline RscWebBrowser example\n\n```cpp\nclass RscWebBrowser\n{\n    type = CT_WEBBROWSER; // 106\n    idc = -1;\n    deletable = 0;\n    style = 0;\n    x = 0;\n    y = 0;\n    w = 0.3;\n    h = 0.3;\n    allowExternalURL = 1;\n    url = \"https://arma3.com\";\n};\n```\n\n### Browser Content\n\nThe browser is separated into two modes:\n\n- Remote content (i.e. loading external resources using a URL), which can be enabled with the allowExternalURL attribute.\n- Local content (i.e. loading local HTML files from within a PBO or mission).\n\n#### Remote Content\n\nRemote content is considered unsafe. Any browser that uses allowExternalURL will prompt the player to allow it being opened.\nURLs are limited by the `allowedHTMLLoadURIs[]` configuration.\n\n#### Local Content\n\nLoading Local Content forces the browser into a sandbox mode.\nPlayers will not receive a permission popup for Local Content.\nLocal Content offers a JavaScript API to interact with the game without having to go through SQF.\nLocal files are subject to `allowedHTMLLoadURIs[]`.\n\n##### Sandbox\n\nThe content is loaded into a [sandboxed](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) `<iframe>` with a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).\n\nAny external communication via [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [XMLHttpRequest](https://developer.mozilla.org/de/docs/Web/API/XMLHttpRequest) is unavailable.\nAccess to [Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is unvailable.\n[alert()](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) and [confirm()](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) are not directly accessible, but the JavaScript API provides a wrapper.\n\nContent like images, styles, fonts, scripts and media can only be specified inline (in case of scripts and styles) or via [Data URLs](https://developer.mozilla.org/en-US/docs/Web/URI/Schemes/data).\n\n##### JavaScript API\n\nThis API is injected into every local content page automatically.\n\nThe API offers the following functions (here expressed as TypeScript to clarify argument and return types):\n\n```javascript\nclass A3API\n{\n    /**\n     * Loads file from game filesystem.\n     *\n     * @param filePath - Path in game filesystem, without leading backslash\n     * @param maxSize - maximum texture width (used to select Mip)\n     * @returns The image as a data-url\n     */\n    static RequestTexture(texturePath: string, maxSize: number): Promise<string>;\n\n    /**\n     * Loads file from game filesystem.\n     *\n     * @param filePath - same as loadFile SQF command\n     * @returns The file content\n     */\n    static RequestFile(filePath: string): Promise<string>;\n\n    /**\n     * Loads and preprocesses file from game filesystem.\n     *\n     * @param filePath - same as preprocessFile SQF command\n     * @returns The file content\n     */\n    static RequestPreprocessedFile(filePath: string): Promise<string>;\n\n    // Triggers a alert() (Needs to be piped due to https://chromestatus.com/feature/5148698084376576)\n    static SendAlert(content: string): void;\n\n    static SendConfirm(content: string): Promise<bool>;\n}\n```\n\nExample, this is how to load a image out of the game, into JavaScript, and insert it into the page\n\n```javascript\nconst eWeaponImage = document.createElement('img');\nA3API.RequestTexture(\"A3\\\\weapons_F\\\\Rifles\\\\MX\\\\data\\\\UI\\\\gear_mx_rifle_X_CA.paa\", 512).then(imageContent => eWeaponImage.src = imageContent);\ndocument.body.appendChild(eWeaponImage);\n```\n\n##### Example HUD\n\nHere is an example of how to create a basic WebBrowser based HUD overlay purely inside a mission file.\n\nCreate a basic mission, and place the following files next to the mission.sqm\n\ndescription.ext:\n\n```cpp\nimport RscText;\nclass RscTitles\n{\n    class HudOverlayUI\n    {\n        idd = 13371337;\n        fadein = 0; // Required parameters for RscTitles\n        fadeout = 0;\n        duration = 1e+011;\n\n        onLoad = \"GHUDOverlay = _this\"; // Store our Display in a variable so we can access it from script\n\n        class controls\n        {\n            class Texture: RscText\n            {\n                type = 106; // CT_WEBBROWSER\n                idc = 1337;\n                x = safeZoneX; // Full screen from corner to corner\n                y = safeZoneY;\n                w = safeZoneW;\n                h = safeZoneH;\n                url = \"file://hudOverlay.html\"; // Reference to a file inside our mission\n            };\n        };\n    };\n};\n```\n\ninit.sqf\n\n```sqf\n(\"MyHudLayer\" call BIS_fnc_rscLayer) cutRsc [\"HudOverlayUI\", \"PLAIN\"]; // Add our Display to the HUD\n\nprivate _ctrl = ((GHUDOverlay#0) displayCtrl 1337);\n\n_ctrl ctrlAddEventHandler [\"JSDialog\", {\n    params [\"_control\", \"_isConfirmDialog\", \"_message\"];\n\n    // Insert message as text into the page, by assembling JavaScript code to insert a new element and set its text.\n    _control ctrlWebBrowserAction [\n        \"ExecJS\",\n        format [\"const eSpan = document.createElement('span'); eSpan.textContent = 'Script says %1!'; document.body.appendChild(eSpan);\", _message]\n    ];\n\n    true; // We need to tell it that we handled the \"dialog\", by returning true or false.\n}];\n\n//_ctrl ctrlWebBrowserAction [\"OpenDevConsole\"]; // Can open developer console here\n//_ctrl ctrlWebBrowserAction [\"LoadFile\", \"hudOverlay.html\"];    // Instead of using url= in description.ext, we could also load the file here,\n                                                                // that way we can open dev console before the page is loaded\n```\n\nhudOverlay.html\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<body>\n\n<script>\n// Load the MX Rifle inventory image and add it to the page\nconst eWeaponImage = document.createElement('img');\nA3API.RequestTexture(\"A3\\\\weapons_F\\\\Rifles\\\\MX\\\\data\\\\UI\\\\gear_mx_rifle_X_CA.paa\", 512).then(imageContent => eWeaponImage.src = imageContent);\ndocument.body.appendChild(eWeaponImage);\n\n// Send a message to script\nA3API.SendAlert(\"JavaScript is sending an alert\"); // This alert will trigger a JSDialog (Alert Dialog) eventhandler\n</script>\n\n</body>\n</html>\n```\n\n##### Seamless Stop / Resume\n\nWhen you stop a browser, and resume it again, the browser will have to reload the last page which might take a couple frames. During the reload, the browser might display a few empty.\nInside a ui2texture containing a browser, it is possible to skip/hide these empty frames by also pausing the ui2texture updates.\n\nThis code assumes that the displayUpdate calls on the ui2texture, are done with a EachFrame Mission EH.\nAnd also the WebBrowser control is directly inside the ui2texture display.\n\n```sqf\n_ctrl ctrlWebBrowserAction [\"StopBrowser\"];\n// Remove the EachFrame handler doing displayUpdate. So our ui2tex will also be frozen\nremoveMissionEventHandler [\"EachFrame\", (ctrlParent _ctrl) getVariable \"displayUpdateHandler\"];\n```\n\n```sqf\n// Resume the browser, it will now start loading the page\nprivate _display = ctrlParent _ctrl;\n_ctrl ctrlWebBrowserAction [\"ResumeBrowser\"];\n\n// Remember when the last browser draw happened, so we can wait for it to become stable (once it stops drawing for a while)\n_ctrl setVariable [\"lastDraw\", diag_frameNo];\nprivate _drawHandlerIndex = _ctrl ctrlAddEventHandler [\"Draw\", {\n    params [\"_ctrl\"];\n    _ctrl setVariable [\"lastDraw\", diag_frameNo]; // Remember when the last draw happened\n}];\n\n// Note that this will not serialize into savegames, and will probably cause errors if Save/Load is involved.\nwaitUntil { ((_ctrl getVariable \"lastDraw\") + 60) < diag_frameNo };\n//#TODO handle the control becoming null because the UI was closed\n\n// No draw for 60 frames, its probably stable, so we resume updating the ui2tex\n\n// Note that this will not serialize into savegames, and will probably cause errors if Save/Load is involved.\nprivate _handlerID = addMissionEventHandler [\"EachFrame\", {displayUpdate (_thisArgs#0)}, [_display]];\n\n_display setVariable [\"displayUpdateHandler\", _handlerID];\n\n_ctrl ctrlRemoveEventHandler [\"Draw\", _drawHandlerIndex]; // Stop listening for draw events\n```\n\n##### Redirecting links into Arma files\n\nYou can build a \"normal\" web page, with \"normal\" `<a href=\"..\">` links on it and have the links navigate to files that are inside Arma 3.\nThis JavaScript will redirect all links to the A3API which will make it load the file from the game filesystem.\nJust like using \"LoadFile\" ctrlWebBrowserAction.\n\n```html\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n    var allLinks = document.getElementsByTagName(\"a\");\n    [...allLinks].forEach((link) => {\n        link.onclick = function (e) {\n            e.preventDefault();\n            var targetUrl = e.currentTarget.getAttribute('href');\n            A3API.NavigateTo(targetUrl);\n        };\n    });\n});\n</script>\n\n<body>\n<a href=\"arma://MissionSubFolder/index.html\">Click me to get to page</a>\n</body>\n```","sections":[{"heading":null,"level":1,"markdown":"Arma 3 v2.20"},{"heading":"Introduction","level":3,"markdown":"Web browser control features an embedded Chromium window.\nThe URL is subject to `allowedHTMLLoadURIs[]` whitelisting in Description.ext - CfgCommands.\nctrlSetURL can be used to change the URL after creation of the control."},{"heading":"Related commands & functions","level":4,"markdown":"- Commands: General\n- Commands: UI Eventhandlers"},{"heading":"Related User Interface Eventhandlers","level":4,"markdown":"- Events: General"},{"heading":"Alphabetical Order","level":3,"markdown":"> [!NOTE]\n> TokenNames common to most controls, such as x, y, w, h, text, idc... can be found **here**.\n\n> [!WARNING]\n> Not all of the listed attributes might have an effect nor might the list be complete. All attributes were gathered with [this](https://github.com/7erra/BIKI.VR/blob/master/fnc/generateCTPage.sqf) config crawler.\n\n```cpp\n#define CT_WEBBROWSER 106\n```"},{"heading":"A","level":4,"markdown":""},{"heading":"allowExternalURL","level":5,"markdown":"**Type:** Boolean\n\n**Description:** Whether this browser will allow external URLs, as opposed to local content.\n\n> [!WARNING]\n> This is an experimental feature currently available for testing on Development Branch.\n> It is not yet decided whether this will ever become available in Stable branch.\n\n```cpp\nallowExternalURL = 1;\n```"},{"heading":"U","level":4,"markdown":""},{"heading":"url","level":5,"markdown":"**Type:** String\n\n**Description:** An external (if allowExternalURL is enabled) URL to be opened, or a path to a local file to load. Local files are subject to `allowedHTMLLoadURIs[]`.\n\n```cpp\nurl = \"https://arma3.com\";\n```"},{"heading":"Scripting","level":3,"markdown":"> [!NOTE]\n> See ctrlWebBrowserAction."},{"heading":"Control Event Handlers","level":4,"markdown":"The following event handlers can only be added to CT_WEBBROWSER controls."},{"heading":"JSDialog","level":5,"markdown":"- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when JavaScript triggers an `alert()` or `confirm()` dialog.\n\n```sqf\nparams [\"_control\", \"_isConfirmDialog\", \"_message\"];\n```\n\nThe code handling this event can return a Boolean true/false. Which will stop the next Eventhandlers from being triggered, and will be interpreted as a reply to the dialog.\nIf the code does not immediately provide a reply, it should return Nothing.\nIf the code does not return bool, then the script must call ctrlWebBrowserAction with the \"JSDialog\" action to reply to the dialog, the WebBrowser will be unresponsive until a reply has been sent."},{"heading":"Draw","level":5,"markdown":"- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when the WebBrowser has drawn a new frame (NOT when the game control displays a new frame)\n\n```sqf\nparams [\"_control\"];\n```"},{"heading":"PageLoaded","level":5,"markdown":"- **Use on:** Control (CT_WEBBROWSER)\n- **Fired on:** Fires when the current page has finished loading. This only contains the main page, the page may contain content (scripts, images) that will load asynchronously and may not be ready. This fires multiple times, when the current page (URL) was changed or when the browser is Resumed.\n\n```sqf\nparams [\"_control\"];\n```\n\nOnce PageLoaded is triggered, it is safe to execute JavaScript on the page. Before PageLoaded, the page might not be ready to receive JavaScript and may drop ExecJS requests."},{"heading":"Default Classes","level":3,"markdown":"> [!NOTE]\n> **Arma 3:** **AddOns:** Classes need to be initialised first with `class SomeClass;`\n>\n> ---\n>\n> **Missions:** Since Arma 3 v2.02 one can use `import SomeClass;` to initialise a class (see the import keyword).\n>\n> In older versions, use `\"Default\" call BIS_fnc_exportGUIBaseClasses;` and paste the result into the description.ext."},{"heading":"RscWebBrowser","level":4,"markdown":"Baseline RscWebBrowser example\n\n```cpp\nclass RscWebBrowser\n{\n    type = CT_WEBBROWSER; // 106\n    idc = -1;\n    deletable = 0;\n    style = 0;\n    x = 0;\n    y = 0;\n    w = 0.3;\n    h = 0.3;\n    allowExternalURL = 1;\n    url = \"https://arma3.com\";\n};\n```"},{"heading":"Browser Content","level":3,"markdown":"The browser is separated into two modes:\n\n- Remote content (i.e. loading external resources using a URL), which can be enabled with the allowExternalURL attribute.\n- Local content (i.e. loading local HTML files from within a PBO or mission)."},{"heading":"Remote Content","level":4,"markdown":"Remote content is considered unsafe. Any browser that uses allowExternalURL will prompt the player to allow it being opened.\nURLs are limited by the `allowedHTMLLoadURIs[]` configuration."},{"heading":"Local Content","level":4,"markdown":"Loading Local Content forces the browser into a sandbox mode.\nPlayers will not receive a permission popup for Local Content.\nLocal Content offers a JavaScript API to interact with the game without having to go through SQF.\nLocal files are subject to `allowedHTMLLoadURIs[]`."},{"heading":"Sandbox","level":5,"markdown":"The content is loaded into a [sandboxed](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox) `<iframe>` with a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).\n\nAny external communication via [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [XMLHttpRequest](https://developer.mozilla.org/de/docs/Web/API/XMLHttpRequest) is unavailable.\nAccess to [Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is unvailable.\n[alert()](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) and [confirm()](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) are not directly accessible, but the JavaScript API provides a wrapper.\n\nContent like images, styles, fonts, scripts and media can only be specified inline (in case of scripts and styles) or via [Data URLs](https://developer.mozilla.org/en-US/docs/Web/URI/Schemes/data)."},{"heading":"JavaScript API","level":5,"markdown":"This API is injected into every local content page automatically.\n\nThe API offers the following functions (here expressed as TypeScript to clarify argument and return types):\n\n```javascript\nclass A3API\n{\n    /**\n     * Loads file from game filesystem.\n     *\n     * @param filePath - Path in game filesystem, without leading backslash\n     * @param maxSize - maximum texture width (used to select Mip)\n     * @returns The image as a data-url\n     */\n    static RequestTexture(texturePath: string, maxSize: number): Promise<string>;\n\n    /**\n     * Loads file from game filesystem.\n     *\n     * @param filePath - same as loadFile SQF command\n     * @returns The file content\n     */\n    static RequestFile(filePath: string): Promise<string>;\n\n    /**\n     * Loads and preprocesses file from game filesystem.\n     *\n     * @param filePath - same as preprocessFile SQF command\n     * @returns The file content\n     */\n    static RequestPreprocessedFile(filePath: string): Promise<string>;\n\n    // Triggers a alert() (Needs to be piped due to https://chromestatus.com/feature/5148698084376576)\n    static SendAlert(content: string): void;\n\n    static SendConfirm(content: string): Promise<bool>;\n}\n```\n\nExample, this is how to load a image out of the game, into JavaScript, and insert it into the page\n\n```javascript\nconst eWeaponImage = document.createElement('img');\nA3API.RequestTexture(\"A3\\\\weapons_F\\\\Rifles\\\\MX\\\\data\\\\UI\\\\gear_mx_rifle_X_CA.paa\", 512).then(imageContent => eWeaponImage.src = imageContent);\ndocument.body.appendChild(eWeaponImage);\n```"},{"heading":"Example HUD","level":5,"markdown":"Here is an example of how to create a basic WebBrowser based HUD overlay purely inside a mission file.\n\nCreate a basic mission, and place the following files next to the mission.sqm\n\ndescription.ext:\n\n```cpp\nimport RscText;\nclass RscTitles\n{\n    class HudOverlayUI\n    {\n        idd = 13371337;\n        fadein = 0; // Required parameters for RscTitles\n        fadeout = 0;\n        duration = 1e+011;\n\n        onLoad = \"GHUDOverlay = _this\"; // Store our Display in a variable so we can access it from script\n\n        class controls\n        {\n            class Texture: RscText\n            {\n                type = 106; // CT_WEBBROWSER\n                idc = 1337;\n                x = safeZoneX; // Full screen from corner to corner\n                y = safeZoneY;\n                w = safeZoneW;\n                h = safeZoneH;\n                url = \"file://hudOverlay.html\"; // Reference to a file inside our mission\n            };\n        };\n    };\n};\n```\n\ninit.sqf\n\n```sqf\n(\"MyHudLayer\" call BIS_fnc_rscLayer) cutRsc [\"HudOverlayUI\", \"PLAIN\"]; // Add our Display to the HUD\n\nprivate _ctrl = ((GHUDOverlay#0) displayCtrl 1337);\n\n_ctrl ctrlAddEventHandler [\"JSDialog\", {\n    params [\"_control\", \"_isConfirmDialog\", \"_message\"];\n\n    // Insert message as text into the page, by assembling JavaScript code to insert a new element and set its text.\n    _control ctrlWebBrowserAction [\n        \"ExecJS\",\n        format [\"const eSpan = document.createElement('span'); eSpan.textContent = 'Script says %1!'; document.body.appendChild(eSpan);\", _message]\n    ];\n\n    true; // We need to tell it that we handled the \"dialog\", by returning true or false.\n}];\n\n//_ctrl ctrlWebBrowserAction [\"OpenDevConsole\"]; // Can open developer console here\n//_ctrl ctrlWebBrowserAction [\"LoadFile\", \"hudOverlay.html\"];    // Instead of using url= in description.ext, we could also load the file here,\n                                                                // that way we can open dev console before the page is loaded\n```\n\nhudOverlay.html\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<body>\n\n<script>\n// Load the MX Rifle inventory image and add it to the page\nconst eWeaponImage = document.createElement('img');\nA3API.RequestTexture(\"A3\\\\weapons_F\\\\Rifles\\\\MX\\\\data\\\\UI\\\\gear_mx_rifle_X_CA.paa\", 512).then(imageContent => eWeaponImage.src = imageContent);\ndocument.body.appendChild(eWeaponImage);\n\n// Send a message to script\nA3API.SendAlert(\"JavaScript is sending an alert\"); // This alert will trigger a JSDialog (Alert Dialog) eventhandler\n</script>\n\n</body>\n</html>\n```"},{"heading":"Seamless Stop / Resume","level":5,"markdown":"When you stop a browser, and resume it again, the browser will have to reload the last page which might take a couple frames. During the reload, the browser might display a few empty.\nInside a ui2texture containing a browser, it is possible to skip/hide these empty frames by also pausing the ui2texture updates.\n\nThis code assumes that the displayUpdate calls on the ui2texture, are done with a EachFrame Mission EH.\nAnd also the WebBrowser control is directly inside the ui2texture display.\n\n```sqf\n_ctrl ctrlWebBrowserAction [\"StopBrowser\"];\n// Remove the EachFrame handler doing displayUpdate. So our ui2tex will also be frozen\nremoveMissionEventHandler [\"EachFrame\", (ctrlParent _ctrl) getVariable \"displayUpdateHandler\"];\n```\n\n```sqf\n// Resume the browser, it will now start loading the page\nprivate _display = ctrlParent _ctrl;\n_ctrl ctrlWebBrowserAction [\"ResumeBrowser\"];\n\n// Remember when the last browser draw happened, so we can wait for it to become stable (once it stops drawing for a while)\n_ctrl setVariable [\"lastDraw\", diag_frameNo];\nprivate _drawHandlerIndex = _ctrl ctrlAddEventHandler [\"Draw\", {\n    params [\"_ctrl\"];\n    _ctrl setVariable [\"lastDraw\", diag_frameNo]; // Remember when the last draw happened\n}];\n\n// Note that this will not serialize into savegames, and will probably cause errors if Save/Load is involved.\nwaitUntil { ((_ctrl getVariable \"lastDraw\") + 60) < diag_frameNo };\n//#TODO handle the control becoming null because the UI was closed\n\n// No draw for 60 frames, its probably stable, so we resume updating the ui2tex\n\n// Note that this will not serialize into savegames, and will probably cause errors if Save/Load is involved.\nprivate _handlerID = addMissionEventHandler [\"EachFrame\", {displayUpdate (_thisArgs#0)}, [_display]];\n\n_display setVariable [\"displayUpdateHandler\", _handlerID];\n\n_ctrl ctrlRemoveEventHandler [\"Draw\", _drawHandlerIndex]; // Stop listening for draw events\n```"},{"heading":"Redirecting links into Arma files","level":5,"markdown":"You can build a \"normal\" web page, with \"normal\" `<a href=\"..\">` links on it and have the links navigate to files that are inside Arma 3.\nThis JavaScript will redirect all links to the A3API which will make it load the file from the game filesystem.\nJust like using \"LoadFile\" ctrlWebBrowserAction.\n\n```html\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n    var allLinks = document.getElementsByTagName(\"a\");\n    [...allLinks].forEach((link) => {\n        link.onclick = function (e) {\n            e.preventDefault();\n            var targetUrl = e.currentTarget.getAttribute('href');\n            A3API.NavigateTo(targetUrl);\n        };\n    });\n});\n</script>\n\n<body>\n<a href=\"arma://MissionSubFolder/index.html\">Click me to get to page</a>\n</body>\n```"}],"categories":["Control Types","Introduced with Arma 3 version 2.20"],"groups":["GUI"]}