CSP and Viewport Tags

The Content-Security-Policy and Viewport Meta Tags

A Content Security Policy (CSP) tells the WebView where the page may load scripts, styles, images and connections from, so injected script cannot pull in more code or send data away. The template's policy allowed https://ssl.gstatic.com 4 (its comments tie it to Android's TalkBack), data: for everything and 'unsafe-eval', none of which BookNest needs, so its policy is now two directives: default-src 'self' limits every kind of load to the app's own origin, and img-src 'self' data: adds inline data: images. 'self' is https://localhost (WebView and Bridge), which covers the fetch() of data/books.json. A request anywhere else fails, and the page can watch the violation happen:

Fetching from another origin under BookNest's policy, run in the WebView consoleJavaScript
const seen = new Promise((resolve) => addEventListener('securitypolicyviolation',
  (e) => resolve(`${e.violatedDirective} blocked ${e.blockedURI}`), { once: true }));
const result = await fetch('https://example.com/books.json').catch((err) => err.message);
[result, await seen];
Output
[
  "Failed to fetch",
  "connect-src blocked https://example.com/books.json"
]

fetch() falls under connect-src, which inherits default-src. When BookNest later talks to a server, add exactly that origin, as in connect-src 'self' https://api.example.com, rather than loosening default-src. Keep scripts in files: without 'unsafe-inline', inline <script> blocks and onclick="..." attributes do not run, which is the point.

The viewport tag, initial-scale=1, width=device-width, viewport-fit=cover, maps one CSS pixel to one density-independent pixel and lets the page extend under cutouts; pad headers with env(safe-area-inset-top).