diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 2a72a4304..a9520679e 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -272,7 +272,8 @@ private void loadWebView() { Logger.warn("Invalid url, using fallback"); } } - localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode); + boolean jsProfilingEnabled = config.isJsProfilingEnabled(); + localServer = new WebViewLocalServer(context, this, injector, authorities, html5mode, jsProfilingEnabled); localServer.hostAssets(DEFAULT_WEB_ASSET_DIR); Logger.debug("Loading app at " + appUrl); diff --git a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java index b20799ba3..99e33b975 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java +++ b/android/capacitor/src/main/java/com/getcapacitor/CapConfig.java @@ -54,6 +54,7 @@ public class CapConfig { private String errorPath; private boolean zoomableWebView = false; private boolean resolveServiceWorkerRequests = true; + private boolean jsProfilingEnabled = false; // Embedded private String startPath; @@ -181,6 +182,7 @@ private CapConfig(Builder builder) { this.errorPath = builder.errorPath; this.zoomableWebView = builder.zoomableWebView; this.resolveServiceWorkerRequests = builder.resolveServiceWorkerRequests; + this.jsProfilingEnabled = builder.jsProfilingEnabled; // Embedded this.startPath = builder.startPath; @@ -286,6 +288,7 @@ private void deserializeConfig(@Nullable Context context) { webContentsDebuggingEnabled = JSONUtils.getBoolean(configJSON, "android.webContentsDebuggingEnabled", isDebug); zoomableWebView = JSONUtils.getBoolean(configJSON, "android.zoomEnabled", JSONUtils.getBoolean(configJSON, "zoomEnabled", false)); resolveServiceWorkerRequests = JSONUtils.getBoolean(configJSON, "android.resolveServiceWorkerRequests", true); + jsProfilingEnabled = JSONUtils.getBoolean(configJSON, "android.jsProfilingEnabled", false); String logBehavior = JSONUtils.getString( configJSON, @@ -382,6 +385,10 @@ public boolean isResolveServiceWorkerRequests() { return resolveServiceWorkerRequests; } + public boolean isJsProfilingEnabled() { + return jsProfilingEnabled; + } + public boolean isWebContentsDebuggingEnabled() { return webContentsDebuggingEnabled; } @@ -582,6 +589,7 @@ public static class Builder { private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION; private boolean zoomableWebView = false; private boolean resolveServiceWorkerRequests = true; + private boolean jsProfilingEnabled = false; // Embedded private String startPath = null; @@ -686,6 +694,11 @@ public Builder setResolveServiceWorkerRequests(boolean resolveServiceWorkerReque return this; } + public Builder setJsProfilingEnabled(boolean jsProfilingEnabled) { + this.jsProfilingEnabled = jsProfilingEnabled; + return this; + } + public Builder setWebContentsDebuggingEnabled(boolean webContentsDebuggingEnabled) { this.webContentsDebuggingEnabled = webContentsDebuggingEnabled; return this; diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index a044bfbe6..a4600acec 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -63,6 +63,7 @@ public class WebViewLocalServer { private final boolean html5mode; private final JSInjector jsInjector; private final Bridge bridge; + private final boolean jsProfilingEnabled; /** * A handler that produces responses for paths on the virtual asset server. @@ -133,13 +134,14 @@ public Map getResponseHeaders() { } } - WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, ArrayList authorities, boolean html5mode) { + WebViewLocalServer(Context context, Bridge bridge, JSInjector jsInjector, ArrayList authorities, boolean html5mode, boolean jsProfilingEnabled) { uriMatcher = new UriMatcher(null); this.html5mode = html5mode; this.protocolHandler = new AndroidProtocolHandler(context.getApplicationContext()); this.authorities = authorities; this.bridge = bridge; this.jsInjector = jsInjector; + this.jsProfilingEnabled = jsProfilingEnabled; } private static Uri parseAndVerifyUrl(String url) { @@ -619,7 +621,13 @@ private void createHostingDetails() { throw new IllegalArgumentException("assetPath cannot contain the '*' character."); } - PathHandler handler = new PathHandler() { + Map customHeaders = null; + if (jsProfilingEnabled) { + customHeaders = new HashMap<>(); + customHeaders.put("Document-Policy", "js-profiling"); + } + + PathHandler handler = new PathHandler(null, null, 200, "OK", customHeaders) { @Override public InputStream handle(Uri url) { InputStream stream = null; diff --git a/cli/src/declarations.ts b/cli/src/declarations.ts index dbb5a326a..11af39252 100644 --- a/cli/src/declarations.ts +++ b/cli/src/declarations.ts @@ -297,6 +297,17 @@ export interface CapacitorConfig { * @default true */ resolveServiceWorkerRequests?: boolean; + + /** + * Enable JavaScript profiling by adding the `Document-Policy: js-profiling` + * HTTP response header to WebView responses. + * + * This header is required for the JS Self-Profiling API to work in the WebView. + * + * @since 8.0.0 + * @default false + */ + jsProfilingEnabled?: boolean; }; ios?: {