Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
Expand Down Expand Up @@ -488,7 +489,15 @@ private WebViewAssetLoader buildAssetLoader(RNCWebViewContainer view, ReadableMa
FLog.w(TAG, "WebViewAssetLoader error. Skipping Path Handler. Directory is missing for internal handler path");
continue;
}
builder.addPathHandler(handlerPath, new WebViewAssetLoader.InternalStoragePathHandler(view.getContext(), new File(directory)));
WebViewAssetLoader.PathHandler internalHandler =
new WebViewAssetLoader.InternalStoragePathHandler(view.getContext(), new File(directory));
// Optional fixed response headers (e.g. Permissions-Policy on a locally served document). The default
// InternalStoragePathHandler sets none, so wrap it when any are configured.
Map<String, String> responseHeaders = readStringMap(handler.getMap("responseHeaders"));
if (!responseHeaders.isEmpty()) {
internalHandler = new HeaderInjectingPathHandler(internalHandler, responseHeaders);
}
builder.addPathHandler(handlerPath, internalHandler);
}
}
} else {
Expand All @@ -498,6 +507,51 @@ private WebViewAssetLoader buildAssetLoader(RNCWebViewContainer view, ReadableMa
return builder.build();
}

private static Map<String, String> readStringMap(@Nullable ReadableMap map) {
Map<String, String> result = new HashMap<>();
if (map == null) {
return result;
}
ReadableMapKeySetIterator iterator = map.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
if (map.getType(key) == ReadableType.String) {
result.put(key, map.getString(key));
}
}
return result;
}

/**
* Wraps a PathHandler and merges a fixed set of response headers onto whatever it serves. InternalStoragePathHandler
* sets no headers, so this lets a locally served document (the embedded-app shell) carry a header-based policy such
* as Permissions-Policy. A header policy on the top-level document applies to its whole frame tree and cannot be
* re-enabled by scripts or a child frame's allow attribute.
*/
private static class HeaderInjectingPathHandler implements WebViewAssetLoader.PathHandler {
private final WebViewAssetLoader.PathHandler delegate;
private final Map<String, String> extraHeaders;

HeaderInjectingPathHandler(WebViewAssetLoader.PathHandler delegate, Map<String, String> extraHeaders) {
this.delegate = delegate;
this.extraHeaders = extraHeaders;
}

@Nullable
@Override
public WebResourceResponse handle(@NonNull String path) {
WebResourceResponse response = delegate.handle(path);
if (response == null) {
return null;
}
Map<String, String> headers = response.getResponseHeaders();
Map<String, String> merged = headers != null ? new HashMap<>(headers) : new HashMap<>();
merged.putAll(extraHeaders);
response.setResponseHeaders(merged);
return response;
}
}


@ReactProp(name = "overScrollMode")
public void setOverScrollMode(RNCWebViewContainer view, String overScrollModeString) {
Expand Down
13 changes: 12 additions & 1 deletion src/WebViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,20 @@ export interface AndroidAssetLoaderPathHandler {
* Certain existing subdirectories of getDataDir are also not permitted as they are often sensitive.
* These files are ("app_webview/", "databases/", "lib/", "shared_prefs/" and "code_cache/").
*
* This field is unused for type 'resources' or 'assets'
* This field is unused for type 'resources' or 'assets'
*/
directory?: string;

/**
* Fixed response headers to merge onto every response this handler serves.
*
* InternalStoragePathHandler sets no headers of its own, so this is the way to attach a header-based policy
* (e.g. `Permissions-Policy`) to a locally served document. A header policy on the top-level document applies to
* its whole frame tree and cannot be re-enabled by scripts or a child frame's `allow` attribute.
*
* Only honored for type 'internal'.
*/
responseHeaders?: {[header: string]: string};
}


Expand Down
Loading