{"id":17972618,"url":"https://github.com/rjmurillo/webview_addallowedwebobjectworkaround","last_synced_at":"2025-03-25T12:33:10.705Z","repository":{"id":70085431,"uuid":"141342593","full_name":"rjmurillo/WebView_AddAllowedWebObjectWorkaround","owner":"rjmurillo","description":"Workaround for lack of AddAllowedWebObject in Win32 WebView","archived":false,"fork":false,"pushed_at":"2022-12-08T02:40:56.000Z","size":69,"stargazers_count":17,"open_issues_count":2,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-24T00:14:48.649Z","etag":null,"topics":["javascript-bridge","javascript-callbacks","javascript-promise","webview","webview-control","winforms","wpf"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rjmurillo.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-17T20:54:17.000Z","updated_at":"2022-03-21T12:43:38.000Z","dependencies_parsed_at":"2023-02-25T20:30:17.486Z","dependency_job_id":null,"html_url":"https://github.com/rjmurillo/WebView_AddAllowedWebObjectWorkaround","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjmurillo%2FWebView_AddAllowedWebObjectWorkaround","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjmurillo%2FWebView_AddAllowedWebObjectWorkaround/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjmurillo%2FWebView_AddAllowedWebObjectWorkaround/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjmurillo%2FWebView_AddAllowedWebObjectWorkaround/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rjmurillo","download_url":"https://codeload.github.com/rjmurillo/WebView_AddAllowedWebObjectWorkaround/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245463018,"owners_count":20619598,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript-bridge","javascript-callbacks","javascript-promise","webview","webview-control","winforms","wpf"],"created_at":"2024-10-29T16:23:52.723Z","updated_at":"2025-03-25T12:33:10.695Z","avatar_url":"https://github.com/rjmurillo.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Getting Started\n\nThe Win32 [WebViewControl](https://docs.microsoft.com/uwp/api/windows.web.ui.interop.webviewcontrol) does not support `ObjectForScripting` or `AddWebAllowedObject`. This sample was designed with the goal of showcasing how communications could be standardized using `window.external.notify` and the [`ScriptNotify`](https://docs.microsoft.com/uwp/api/windows.web.ui.interop.webviewcontrol.scriptnotify) event.\n\n## Clone\n\nClone this repository or download the source code. You will need the contents of **JavaScriptBridge** for this sample.\n\n## Create new project\n\n- Create a new WinForms or WPF project targeting **.NET Framework 4.6.2** or later. Samples are given for both WinForms and WPF, and .NET 4.6.2 is required for the WebView NuGet package.\n- Install **Microsoft.Toolkit.Win32.UI.Controls** NuGet package to project.\n- Copy **JavaScriptBridge** project from clone to your solution\n- Use **Add existing project** to add the copied **JavaScriptBridge** project to your new solution.\n- From your WinForms or WPF project, use **Add reference** to add the **JavaScriptBridge** project.\n\n## Add a WebView\n\nWhen using Visual Studio 15.8, the WebView control shows up automatically in your toolbox. If using an older version of Visual Studio, use the instructions in the documentation to [Add the WebView control to the Visual Studio Toolbox](https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/webview#add-the-webview-control-to-the-visual-studio-toolbox).\n\n### Basic Configuration For WPF\n\nFor WPF we assume a WebView has been added with the name *WebView*.\n\n**MainWindow.xaml.cs**\n\n```csharp\nusing System.Windows;\n\nnamespace AddWebAllowedObject_GettingStarted\n{\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n\n             WebView.DOMContentLoaded += (o, e) =\u003e Title = WebView.DocumentTitle;\n        }\n\n        private void WebView_OnLoaded(object sender, RoutedEventArgs e)\n        {\n            WebView.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\n### Basic Configuration For WinForms\n\nFor WinForms we assume a WebView has been added via the designer with the default name *webView1*.\n\n**Form1.cs**\n\n```csharp\nusing System.Windows.Forms;\n\nnamespace WindowsFormsApp1\n{\n    public partial class Form1 : Form\n    {\n        public Form1()\n        {\n            InitializeComponent();\n\n            webView1.DOMContentLoaded += (o, e) =\u003e Text = webView1.DocumentTitle;\n\n            webView1.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\n## Add Local Content\n\nIn the previous step we use the method `NavigateToLocal(String)` method to navigate to content. To create that content:\n\n- Add a new HTML page to your project called **Content.html**.\n- After adding, ensure the property **Copy to Output Directory** is set to **Copy if newer**.\n- Replace the default content with the following\n\n**Content.html**\n\n```html\n\u003c!DOCTYPE html\u003e\n\n\u003chtml lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"\u003e\n\u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003ctitle\u003eJavaScript Bridge: Getting Started\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cbutton id=\"hello-world\"\u003eHello, World!\u003c/button\u003e\n    \u003cdiv id=\"output\"\u003e\u003c/div\u003e\n\n    \u003cscript\u003e\n        var ConnectWebViewBridge = function (callback) {\n            if (window.JavaScriptBridge) {\n                callback(JavaScriptBridge);\n            } else {\n                // Bridge is not yet loaded, wait for event\n                document.addEventListener(\n                    \"JavaScriptBridgeReady\",\n                    function () {\n                        callback(JavaScriptBridge);\n                    },\n                    false);\n            }\n        };\n\n        ConnectWebViewBridge(function (bridge) {\n            console.log(\"JavaScript bridge is ready!\");\n        });\n    \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe content includes basic HTML elements, and the `script` block used to wait for the JavaScript bridge to initialize. You can place it wherever you would like, but it is placed in this sample at the end of the `body` tag so as not to block any other scripts, and the DOM has the ability to get ready before parsing and invoking our script.\n\nIf you run the sample at this point, there should be an empty window with a title of *JavaScript Bridge Getting Started*.\n\n## Debugging your WebView\n\nDuring the course of development you may wish to debug the content in the WebView instance. To do that, you will need to use the [Microsoft Edge DevTools Preview](https://www.microsoft.com/store/productId/9MZBFRMZ0MNJ), available from the Microsoft Store.\n\n- Install [Microsoft Edge DevTools Preview](https://www.microsoft.com/store/productId/9MZBFRMZ0MNJ) from the Microsoft Store.\n- Launch your application.\n- Launch **Microsoft Edge DevTools Preview**\n- Look for a **Local** **Debug Target** with the title **JavaScript Bridge Getting Started** with the URI **ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html**\n- Click that item to attach the debugger. Once attached a new window will pop up to debug your local content within the WebView.\n\nOnce the debugger is attached, we will need the URI to configure the JavaScript bridge. You can easily get the URI of the document by following these steps\n\n- With the Microsoft Edge DevTools Preview attached to your WebView instance, click on the **Console** tab.\n- Next to the `\u003e` symbol, type `document.URL` which will output `\"ms-local-stream://Microsoft.Win32WebViewHost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html\"`\n- Copy the returned value. We'll use that in the configuration step.\n- After the value has been copied, close the debugger and your application instance.\n\n## Inject the JavaScript bridge into your content\n\nInjecting the JavaScript bridge into your content requires two items: an instance of `IWebView`, which we have added in a previous step, and the expected location of content that we will listen to [`IWebView.ScriptNotify`](https://docs.microsoft.com/uwp/api/windows.web.ui.interop.webviewcontrol.scriptnotify) events, which we just received from the debugger.\n\nTo configure the JavaScript bridge, return to your .NET code-behind and replace the contents with the following.\n\n### Adding JavaScript Bridge for WPF\n\n**MainWindow.xaml.cs**\n\n```csharp\nusing System;\nusing System.Windows;\nusing Microsoft.Toolkit.Win32.UI.Controls.WebViewExtensions;\n\nnamespace AddWebAllowedObject_GettingStarted\n{\n    public partial class MainWindow : Window\n    {\n        private JavaScriptBridge _javaScriptBridge;\n\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            _javaScriptBridge = JavaScriptBridge.CreateAndStart(\n                WebView,\n                new Uri(\"ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html\"));\n\n            WebView.DOMContentLoaded += (o, e) =\u003e Title = WebView.DocumentTitle;\n        }\n\n        private void WebView_OnLoaded(object sender, RoutedEventArgs e)\n        {\n            WebView.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\n### Adding JavaScript Bridge for WinForms\n\n**Form1.cs**\n\n```csharp\nusing Microsoft.Toolkit.Win32.UI.Controls.WebViewExtensions;\nusing System;\nusing System.Windows.Forms;\n\nnamespace WindowsFormsApp1\n{\n    public partial class Form1 : Form\n    {\n        private JavaScriptBridge _javaScriptBridge;\n\n        public Form1()\n        {\n            InitializeComponent();\n\n            _javaScriptBridge = JavaScriptBridge.CreateAndStart(\n                webView1,\n                new Uri(\"ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html\"));\n\n\n            webView1.DOMContentLoaded += (o, e) =\u003e Text = webView1.DocumentTitle;\n\n            webView1.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\nAfter initialization, let's check to make sure everything is working by debugging the WebView:\n\n- Launch your application\n- Launch **[Microsoft Edge DevTools Preview](https://www.microsoft.com/store/productId/9MZBFRMZ0MNJ)**\n- Look for a **Local** **Debug Target** with the title **JavaScript Bridge Getting Started** with the URI **ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html**\n- Click that item to attach the debugger. Once attached a new window will pop up to debug your local content within the WebView.\n- Click on the **Console** tab of the debugger.\n- There should be a message `JavaScript bridge is ready!`\n\nIf you see the ready message, the JavaScript bridge was successfully injected into the running WebView instance.\n\n## Calling .NET methods from JavaScript\n\nNow that everything is configured and the JavaScript bridge is injected into your content page, we can begin using it by configuring a scripting handler. A script handler takes two pieces of information, an identifier, and a delegate to execute with named parameters and an optional return value. To register a script handler from our .NET code, use the `JavaScriptBridge.AddScriptingHandler` method.\n\n### Add Scripting Handler for WPF\n\n**MainWindow.xaml.cs**\n\n```csharp\nusing System;\nusing System.Windows;\nusing Microsoft.Toolkit.Win32.UI.Controls.WebViewExtensions;\n\nnamespace AddWebAllowedObject_GettingStarted\n{\n    public partial class MainWindow : Window\n    {\n        private JavaScriptBridge _javaScriptBridge;\n\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            _javaScriptBridge = JavaScriptBridge.CreateAndStart(\n                WebView, \n                new Uri(\"ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html\"));\n            _javaScriptBridge.AddScriptingHandler(\n                \"HelloWorld\", \n                @params =\u003e \"Hello, World!\");\n\n            WebView.DOMContentLoaded += (o, e) =\u003e Title = WebView.DocumentTitle;\n        }\n\n        private void WebView_OnLoaded(object sender, RoutedEventArgs e)\n        {\n            WebView.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\n### Add Scripting Handler for WinForms\n\n**Form1.cs**\n\n```csharp\nusing Microsoft.Toolkit.Win32.UI.Controls.WebViewExtensions;\nusing System;\nusing System.Windows.Forms;\n\nnamespace WindowsFormsApp1\n{\n    public partial class Form1 : Form\n    {\n        private JavaScriptBridge _javaScriptBridge;\n\n        public Form1()\n        {\n            InitializeComponent();\n\n            _javaScriptBridge = JavaScriptBridge.CreateAndStart(\n                webView1,\n                new Uri(\"ms-local-stream://microsoft.win32webviewhost_cw5n1h2txyewy_4c6f63616c436f6e74656e74/Content.html\"));\n            _javaScriptBridge.AddScriptingHandler(\n                \"HelloWorld\",\n                @params =\u003e \"Hello, World!\");\n\n            webView1.DOMContentLoaded += (o, e) =\u003e Text = webView1.DocumentTitle;\n\n            webView1.NavigateToLocal(\"/Content.html\");\n        }\n    }\n}\n```\n\nA scripting handler is registered with the id *HelloWorld*, which then specifies a lambda taking the named parameters called *@params*, which is a `Dictionary\u003cstring, object\u003e` instance.\n\n## Calling HelloWorld scripting handler\n\nTo invoke our registered scripting handler *HelloWorld*, we need to invoke the `callNative` method on the JavaScript bridge injected into our page. For our sample we will handle the click event of the **Hello, World!** button and call the JavaScript bridge.\n\nThe signature for `callNative` accepts a handlerId, data to pass that handler in the form of named arguments, a success callback function, and an error callback function. The bridge accepts three types of invocations: callbacks, JavaScript Promises, and async/await syntax.  Each implementation is shown in that order and you can pick the one that matches your coding style.\n\n### Hello World using JavaScript Callbacks\n\nThis example only passes in the handlerId and a success function.\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        bridge.callNative(handlerId, function(response) {\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n        });\n    };\n});\n```\n\n### Hello World using JavaScript Promise\n\nThe JavaScript bridge also supports Promises. As before, we invoke `callNative`, which returns a `Promise` (the eventual result) instead of our value.\n\nThis example passes in the handlerId as before, and uses `.then` to handle success.\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        bridge.callNative(handlerId)\n              .then(function (response) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;});\n    };\n});\n```\n\n### Hello World using JavaScript Async/Await\n\nThe JavaScript bridge also supports the async/await syntax. As before, we invoke `callNative`, which returns a `Promise`, that we then `await`.\n\nThis example passes in the handlerId as before, awaits the response, then outputs to the log as before.\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = async function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const response = await bridge.callNative(handlerId);\n        const log = document.getElementById(\"output\");\n        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n    };\n});\n```\n\nRun your project. After clicking the *Hello, World!* button, you should see the text `Hello, World!` below the button.\n\n## Calling .NET methods with parameters\n\nSo far we have used the `HelloWorld` script handler, which takes no parameters and returns a string. Let's now refactor that delegate to take a single parameter, `name`, and include that in the response.\n\nLet's go back to our .NET code and change the scripting handler from\n\n```csharp\n_javaScriptBridge.AddScriptingHandler(\n    \"HelloWorld\",\n    @params =\u003e \"Hello, World!\");\n```\n\nto the following\n\n```csharp\n_javaScriptBridge.AddScriptingHandler(\n    \"HelloWorld\",\n    @params =\u003e $\"Hello, {@params[\"name\"]}!\");\n```\n\nWe also need to change the JavaScript to pass in the data. `callNative` includes a parameter for passing the handler data, after passing the handler id. The data is expected to be passed as a set of key/value pairs as a JavaScript object.\n\n```javascript\nconst handlerData = { name: \"World\" };\n```\n\nThe data must be passed as a set of key/value pairs. In the above example, a new JavaScript object with the property `name` is created, with a string value of *World*. This is deserialized by the bridge into a `Dictionary\u003cstring,object\u003e` that is passed in as the *@params* parameter.\n\nHere is what the inclusion of the data parameter looks like in the various syntaxes in JavaScript.\n\n### Hello World with Parameters using JavaScript Callback\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n        helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n        bridge.callNative(handlerId, handlerData, function(response) {\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n        });\n    };\n});\n```\n\n### Hello World with Parameters using JavaScript Promise\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n        bridge.callNative(handlerId, handlerData)\n              .then(function (response) {\n                     const log = document.getElementById(\"output\");\n                    log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                });\n    };\n});\n```\n\n### Hello World with Parameters using JavaScript Async/Await\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = async function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n        const response = await bridge.callNative(handlerId, handlerData);\n        const log = document.getElementById(\"output\");\n        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n        };\n    });\n```\n\nIf you run the project you'll see the familiar `Hello, World!` text below the button. If you change the handler data to something else (say, your name), you would expect to see something like `Hello, Richard!` below the button.\n\n## Exception Handling\n\nIt may be the case that when calling .NET code an exception is encountered. This is handled automatically by the JavaScript bridge and is raised as either a callback, JavaScript Promise reject, or an exception that can be caught with async/await. In each case an object representing the .NET exception is returned via the bridge.\n\nLet's go back to our .NET code and change the scripting handler from\n\n```csharp\n_javaScriptBridge.AddScriptingHandler(\n    \"HelloWorld\",\n    @params =\u003e $\"Hello, {@params[\"name\"]}!\");\n```\n\nto the following\n\n```csharp\n_javaScriptBridge.AddScriptingHandler(\"HelloWorld\", @params =\u003e\n{\n    if (@params == null)\n    {\n        throw new ArgumentNullException(nameof(@params));\n    }\n\n    if (@params.Count != 1)\n    {\n        throw new ArgumentOutOfRangeException(nameof(@params), \"Expected one parameter.\");\n    }\n\n    if (!@params.ContainsKey(\"name\"))\n    {\n        throw new ArgumentOutOfRangeException(nameof(@params), \"Expected parameter 'name'.\");\n    }\n\n    return $\"Hello, {@params[\"name\"]}!\";\n});\n```\n\n\u003eWhen implementing this in your project it is always a good idea to check the parameters coming from JavaScript.\n\nTo handle the error events, the JavaScript code needs to be altered to provide an error callback or catch the exception from the `Promise`. Examples are given below for each syntax.\n\n### Handling Exceptions using JavaScript Callback\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n\n        bridge.callNative(\n                    handlerId,\n                    handlerData,\n                    function(response) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.info(response);\n                    }, function(err) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.error(err.message);\n                    });\n    };\n});\n```\n\n### Handling Exceptions using JavaScript Promise\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n\n        bridge.callNative(handlerId, handlerData)\n              .then(function(response) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.info(response);})\n               .catch(function(err) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.error(err.message);});\n    };\n});\n```\n\n### JavaScript Async/Await\n\n**Content.html**\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = async function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = { name: \"World\" };\n        try {\n            const response = await bridge.callNative(handlerId, handlerData);\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n            console.info(response);\n        } catch (err) {\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n            console.error(err.message);\n        }\n    };\n});\n```\n\nIf you run the project the output should appear as before: `Hello, World!`. To see the error handler in action, change the JavaScript code from\n\n```javascript\nconst handlerData = { name: \"World\" };\n```\n\nto\n\n```javascript\nconst handlerData = { name2: \"World\" };\n```\n\nSince the .NET script handler is expecting a dictionary key the name \"*name*\", after clicking the button you should see the following on the screen\n\n```json\n{\n  \"message\": \"Expected parameter 'name'\\\\r\\\\nParameter name: params\",\n  \"data\": {},\n  \"source\": \"JavaScript Bridge\",\n  \"hResult\": -2146233088\n}\n```\n\n## Returning Complex Objects\n\nThe JavaScript bridge is also capable of returning .NET objects and structures, so long as they can be serialized to JSON. Let's return to our .NET code and update the scripting handler.\n\n```csharp\n_javaScriptBridge.AddScriptingHandler(\"HelloWorld\", @params =\u003e\n{\n    if (@params == null)\n    {\n        throw new ArgumentNullException(nameof(@params));\n    }\n\n    if (@params.Count == 0 || @params.Count \u003e 3)\n    {\n        throw new ArgumentOutOfRangeException(nameof(@params), \"Expected three or less parameters\");\n    }\n\n    var retval = new AddressBookEntry();\n\n    if (@params.ContainsKey(\"firstName\"))\n    {\n        retval.FirstName = @params[\"firstName\"]?.ToString();\n    }\n\n    if (@params.ContainsKey(\"lastName\"))\n    {\n        retval.LastName = @params[\"lastName\"]?.ToString();\n    }\n\n    if (@params.ContainsKey(\"company\"))\n    {\n        retval.Company = @params[\"company\"]?.ToString();\n    }\n\n    if (string.IsNullOrEmpty(retval.FullName))\n    {\n        throw new InvalidOperationException(\"Parameters did not produce a valid name.\");\n    }\n\n    return retval;\n});\n```\n\nThe handler includes checks on the number of parameters as before, and builds up a .NET type `AddressBookEntry` based on the keys of the dictionary passed in. To compile the sample, we'll also need to add the `AddressBookEntry` class to the project.\n\n```csharp\npublic class AddressBookEntry\n{\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n    public string Company { get; set; }\n    public string FullName\n    {\n        get\n        {\n            var fnEmpty = string.IsNullOrEmpty(FirstName);\n            var lnEmpty = string.IsNullOrEmpty(LastName);\n            var cEmpty = string.IsNullOrEmpty(Company);\n\n            if (fnEmpty \u0026\u0026 lnEmpty \u0026\u0026 !cEmpty)\n            {\n                return Company;\n            }\n\n            if (!fnEmpty || !lnEmpty)\n            {\n                var sb = new StringBuilder(256);\n                if (!fnEmpty)\n                {\n                    sb.Append(FirstName);\n                    sb.Append(\" \");\n                }\n\n                if (!lnEmpty)\n                {\n                    sb.Append(LastName);\n                    sb.Append(\" \");\n                }\n\n                if (!cEmpty)\n                {\n                    sb.Append(\"(\");\n                    sb.Append(Company);\n                    sb.Append(\")\");\n                }\n\n                return sb.ToString().Trim();\n            }\n\n            return string.Empty;\n        }\n    }\n\n    public override string ToString()\n    {\n        return FullName;\n    }\n}\n```\n\nIf we run the sample without changing the JavaScript, the following error is returned\n\n```json\n{\n  \"message\": \"Parameters did not produce a valid name.\",\n  \"data\": {},\n  \"source\": \"JavaScript Bridge\",\n  \"hResult\": -2146233088\n}\n```\n\nThe new delegate function expects some additional parameters to be sent in order to build the `AddressBookEntry`. Since one parameter was sent, the dictionary had one key, but it was not a key that was usable by the delegate. As such, it returned a `InvalidOperationException` instead of an empty instance of `AddressBookEntry`.\n\nTo take advantage of the new `AddressBookEntry` object, updates need to be made to the JavaScript. As before, data is passed in as key/value pairs.\n\n```javascript\nconst handlerData = {\n                firstName: \"Hello\",\n                lastName: \"World\"\n        };\n```\n\n### Multiple Parameters using JavaScript Callback\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = {\n                firstName: \"Hello\",\n                lastName: \"World\"\n        };\n\n        bridge.callNative(\n                handlerId,\n                handlerData,\n                function(response) {\n                    const log = document.getElementById(\"output\");\n                    log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                    console.info(response);\n                }, function(err) {\n                    const log = document.getElementById(\"output\");\n                    log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                    console.error(err.message);\n                });\n    };\n});\n```\n\n### Multiple Parameters using JavaScript Promise\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = {\n                firstName: \"Hello\",\n                lastName: \"World\"\n        };\n\n        bridge.callNative(handlerId, handlerData)\n              .then(function(response) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.info(response);\n                })\n                .catch(function (err) {\n                        const log = document.getElementById(\"output\");\n                        log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n                        console.error(err.message);\n                });\n    };\n});\n```\n\n### Multiple Parameters using JavaScript Async/Await\n\n```javascript\nConnectWebViewBridge(function(bridge) {\n    console.log(\"JavaScript bridge is ready!\");\n\n    const helloWorldButton = document.getElementById(\"hello-world\");\n    helloWorldButton.onclick = async function(e) {\n        e.preventDefault();\n\n        const handlerId = \"HelloWorld\";\n        const handlerData = {\n                firstName: \"Hello\",\n                lastName: \"World\"\n        };\n\n        try {\n            const response = await bridge.callNative(handlerId, handlerData);\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(response, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n            console.info(response);\n        } catch (err) {\n            const log = document.getElementById(\"output\");\n            log.innerHTML = `\u003cpre\u003e\u003ccode\u003e${JSON.stringify(err, null, 2)}\u003c/code\u003e\u003c/pre\u003e`;\n            console.error(err.message);\n        }\n    };\n});\n```\n\nRunning the project will a JSON response of the serialized `AddressBookEntry` class.\n\n```json\n{\n  \"firstName\": \"Hello\",\n  \"lastName\": \"World\",\n  \"fullName\": \"Hello World\"\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frjmurillo%2Fwebview_addallowedwebobjectworkaround","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frjmurillo%2Fwebview_addallowedwebobjectworkaround","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frjmurillo%2Fwebview_addallowedwebobjectworkaround/lists"}