{"id":15345902,"url":"https://github.com/niklasmerz/explainer-webview-local-content","last_synced_at":"2026-02-03T16:10:59.440Z","repository":{"id":73559091,"uuid":"591072318","full_name":"NiklasMerz/explainer-webview-local-content","owner":"NiklasMerz","description":"Draft repo for WebViewCG explainer - Please submit PRs or discussions for feedback","archived":false,"fork":false,"pushed_at":"2023-03-27T17:34:10.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-01T02:46:39.428Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NiklasMerz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2023-01-19T21:28:09.000Z","updated_at":"2023-01-21T10:51:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"34315fcb-094c-41ea-8ccb-a39e1c0a5537","html_url":"https://github.com/NiklasMerz/explainer-webview-local-content","commit_stats":{"total_commits":17,"total_committers":2,"mean_commits":8.5,"dds":"0.11764705882352944","last_synced_commit":"e1df9e334b9f9b0c38599b56852098ec8642b119"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/NiklasMerz/explainer-webview-local-content","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NiklasMerz%2Fexplainer-webview-local-content","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NiklasMerz%2Fexplainer-webview-local-content/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NiklasMerz%2Fexplainer-webview-local-content/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NiklasMerz%2Fexplainer-webview-local-content/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NiklasMerz","download_url":"https://codeload.github.com/NiklasMerz/explainer-webview-local-content/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NiklasMerz%2Fexplainer-webview-local-content/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262003966,"owners_count":23243350,"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":[],"created_at":"2024-10-01T11:17:24.058Z","updated_at":"2026-02-03T16:10:57.936Z","avatar_url":"https://github.com/NiklasMerz.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Providing locally hosted content to WebViews explainer\n\n## Authors:\n\n- Niklas Merz, Apache Software Foundation\n\n## Use-case / Challenge\n\n[The Origin in a WebView for locally hosted content](https://webview-cg.github.io/usage-and-challenges/#the-origin-in-a-webview-for-locally-hosted-content)\n\n## Introduction\n\nWebViews are widely used for building apps on the dominating mobile and desktop platforms. Up to 30% of apps found in the app stores (Apple and Google) are built with frameworks like Apache Cordova and CapacitorJS. For example those two frameworks use one big WebView for providing app developers a native wrapper and some plugins for their Web app. App developers build their Web application and put the HTML, CSS and JavaScript files in one folder. The framework then takes care of building a native app project and bundling the Web code as a native application ready to distribute via the app stores.\n\nUsing this approach the native app is responsible for \"hosting\" the content for the WebView. Traditionally the file protocol (`file:/path/to/content`) was often used to load the Web content from the app bundle. Modern web APIs and some frontend frameworks don't work well with pages loaded from `file:`. This led WebView vendors to build their own APIs for app developers to load their content into the WebView. The existing APIs have each different capabilities and URL schemes, which imposes challenges to developers.\n\n## Goals\n\nThe goal of this explainer is to propose a new API for WebViews to serve web content from the local file system. App developers should be able to use the same origin and capabilities to provide their web content to WebViews on different platforms. This also includes an API to intercept and modify requests \u0026 responses to certain URLs for delivering web content and sharing HTTP requests between the WebView and native layer.\n\n## Non-goals\n\n\u003c!-- If there are \"adjacent\" goals which may appear to be in scope but aren't,\nenumerate them here. This section may be fleshed out as your design progresses, and you encounter necessary technical and other trade-offs. --\u003e\n\nProviding an API that can be used as a network interceptor/proxy for requests to arbitrary URLs is not in scope of this explainer.   \n\n## API proposals\n\nThe code examples for the API proposals are written in JavaScript for demonstration purpose only. WebView APIs typically are provided in one or more languages typically used on the platform respectively. The sample code should just provide an idea how the proposed capabilities should look like.\n\n### Register custom scheme\n\nA WebView implementation should allow registering custom schemes like `appname://`, `localcontent:\\\\` etc. to be used for hosting the local files.\n\n```js\n\nconst webview = new WebView()\n\n// webview should intercept request to \"myapp://\"\nwebview.registerScheme('myapp')\n\n```\n\n\u003c!-- Where necessary, provide links to longer explanations of the relevant pre-existing concepts and API.\nIf there is no suitable external documentation, you might like to provide supplementary information as an appendix in this document, and provide an internal link where appropriate. --\u003e\n\n\n### Intercept \u0026 modify request + response\n\nThis API allows you to interact with HTTP request to your custom scheme.\n\n```js\n\nwebview.shouldInterceptRequest((request) =\u003e{\n\n    // Intercept only request to \"myapp://localcontent\" and create new reponse\n    if (request.hostname == 'localcontent') {\n\n        // Here you could build a new reponse from locally loaded files or fetched files from the native layer\n        response = new Reponse('MY HTML CONTENT HERE');\n        reponse.setCookie('intercepted', 'true')\n        reponse.setHeader('Intercepted', 'true');\n\n        return reponse;\n    }\n\n    // Request should not be handled here and is handled normally by the WebView\n    return undefined;\n})\n\n```\n\nThe callback function of `shouldInterceptRequest` receives all requests to registered custom schemes and returns the desired response. If no response is given (null value) the WebView falls back to its regular behavior.\n\n### Default asset loader\n\nThere could be an integrated API for just loading files from a directory with no further interaction needed.\n\n```js\n\nconst webview = new WebView()\n\nwebview.registerPathHanlder('myapp', '/path/to/local/files')\n\n```\n\n## Key scenarios\n\n\u003c!--If there are a suite of interacting APIs, show how they work together to solve the key scenarios described. --\u003e\n\n### Hybrid Apps\n\nA popular use case for WebViews are so-called hybrid apps. This app development concept means that a native app consists of one WebView that is used to render the entire apps UI. These apps very often load their web content (HTML, CSS JavaScript) files from the local file system.\n\nTraditionally the `file:` protocol could be used for loading the files just like normal browsers do. A couple of reasons and changes in common web engine behavior and frontend development make this no longer feasible. Namely, some things are:\n\n* Popular single page application frameworks like React, Angular etc. build their JavaScript based internal routing systems using HTML5 round and won't work with `file:` URLs.\n* JavaScript HTTP requests using XMLHttpRequest or fetch (also known as AJAX) don't work well with `file:` URLs. Web engines consider `file:` URLs a privacy-sensitive context and set the \"Origin\" of the header to `null` following [RFC-6454](https://www.rfc-editor.org/rfc/rfc6454#section-7.3). This makes [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) request difficult or impossible. Hybrid apps often use local files for the UI hosted on one local origin and access REST APIs hosted on another remote origin.\n\nThe proposed APIs can be used to load local content on a regular, non-privacy-sensitive origin and therefore allow developers to build their web application just like for a regular browser context.\n\n\u003e TODO explain flow with code examples\n\n### Custom HTTP requests in the native layer\n\nIn some use cases developers using WebViews might want to intercept or modify requests to certain URLs. Some examples:\n\n* **Session sharing**: If a user logins into a native app and opens a WebView it should be able to use the same session as the native user session and vice versa. The proposed interception API can be used to extract or add authentication information for requests to certain URLs to share them with the native layer to make it possible do HTTP requests with the same authentication information in native or Web context. Authentication information are typically cookies or auth tokens in headers that could be modified by the interception API.\n* **Additional Security**: HTTP Public Key Pinning\n* **InAppUpdates**: Apps might want to load content from servers instead of the local files. This could be for offline mode implementations or getting newer versions without updating the complete app.\n\n## Detailed design discussion\n\n### Custom scheme vs HTTP(S) Origin\n\n\u003c!-- Talk through the tradeoffs in coming to the specific design point you want to make. --\u003e\n\nThere are different options for the special URL that should be handled by this API. Both options are used right now by existing WebView implementations.\n\n```js\nconst webview = new WebView()\n\nwebview.registerScheme('myapp') // Used by iOS WKURLSchemeHandler\n\n// or \n\nwebview.registerScheme('https://localcontent.mydomain.com') // Used by Android WebViewAssetLoader\n```\n\nThe custom scheme variant is straightforward, because this avoids conflicts with existing internet addresses. Officially registered and non-standard custom schemes are widely used for different purposes in apps.\n\nThe standard HTTP(S) scheme probably offers better flexibility and compatability with Web APIs and standards such as CSP and CORS. It also allows use-cases where the WebViews falls back or overwrites certain URLs.\n\n**Both variants could be allowed.**\n\n\u003e This design decision is up for discussion: https://github.com/NiklasMerz/explainer-webview-local-content/discussions/1\n\n\u003c!-- This may be an open question,\nin which case you should link to any active discussion threads. --\u003e\n\n### Origin\n\nEach registered origin like `https://foo` and `https://bar` should be treated as their own origin like \"regular\" Web pages in browsers.\n\n### Proxy API\n\n\u003e URL handler can be used for a proxy-like implementation of all web requests from the WebView through the native layer\n\nThe proposed API can be used to proxy Web requests by rewriting the URL to one used by an handler and using that handler to create HTTP requests in the native layer.\n\n## Considered alternatives\n\n\u003c!-- This should include as many alternatives as you can,\nfrom high level architectural decisions down to alternative naming choices. --\u003e\n\n### WebViews don't consider the `file:` protocol as privacy-sensitive content.\n\n\u003e TODO research standards \u0026 Android + iOS WebView changes \n\n[RFC 6454 The Web Origin Concept](https://www.rfc-editor.org/rfc/rfc6454#section-7.3) does not define privacy-sensitive contexts therefore WebViews could consider setting the Origin header with a valid value to avoid the issues with CORS requests as mentioned before.\n\n\n## References \u0026 acknowledgements\n\n\u003c!-- Your design will change and be informed by many people; acknowledge them in an ongoing way! It helps build community and, as we only get by through the contributions of many, is only fair.\nUnless you have a specific reason not to, these should be in alphabetical order. --\u003e\n\nThe proposed APIs and features are largely inspired by [Androids WebViewAssetLoader](https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader) and [iOS' WKURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkurlschemehandler). This explainer tries to combine the best of both approaches into common APIs for every WebView platform.\n\n\nMany thanks for valuable feedback and advice from:\n\n* Rayan Kanso\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklasmerz%2Fexplainer-webview-local-content","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fniklasmerz%2Fexplainer-webview-local-content","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fniklasmerz%2Fexplainer-webview-local-content/lists"}