@crossbind/core-app-host 0.0.0-bootstrap.0 → 2.0.0-beta.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +55 -3
  2. package/android/app/src/main/AndroidManifest.xml +15 -0
  3. package/android/app/src/main/java/dev/crossbind/app/CrossbindActivity.java +366 -0
  4. package/android/app/src/main/res/xml/network_security_config.xml +8 -0
  5. package/android/build.gradle +14 -0
  6. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  7. package/android/gradle/wrapper/gradle-wrapper.properties +10 -0
  8. package/android/gradle.properties +2 -0
  9. package/android/gradlew +248 -0
  10. package/android/gradlew.bat +112 -0
  11. package/android/settings.gradle +14 -0
  12. package/cpp/CMakeLists.txt +110 -0
  13. package/cpp/src/android/jni.cpp +167 -0
  14. package/cpp/src/app.cpp +40 -0
  15. package/cpp/src/app.h +21 -0
  16. package/cpp/src/attach.cpp +40 -0
  17. package/cpp/src/darwin/bundle.mm +19 -0
  18. package/cpp/src/darwin/channels.mm +196 -0
  19. package/cpp/src/darwin/navigation.mm +69 -0
  20. package/cpp/src/host_core.cpp +335 -0
  21. package/cpp/src/host_core.h +84 -0
  22. package/cpp/src/ios/bundle.mm +15 -0
  23. package/cpp/src/ios/main.mm +71 -0
  24. package/cpp/src/linux/app.cpp +29 -0
  25. package/cpp/src/linux/channels.cpp +143 -0
  26. package/cpp/src/linux/main_thread.h +16 -0
  27. package/cpp/src/linux/navigation.cpp +55 -0
  28. package/cpp/src/main.cpp +57 -0
  29. package/cpp/src/platform.h +57 -0
  30. package/cpp/src/windows/app.cpp +36 -0
  31. package/cpp/src/windows/app.manifest +8 -0
  32. package/cpp/src/windows/app.rc +3 -0
  33. package/cpp/src/windows/channels.cpp +336 -0
  34. package/cpp/src/windows/com.cpp +92 -0
  35. package/cpp/src/windows/com.h +57 -0
  36. package/cpp/src/windows/navigation.cpp +90 -0
  37. package/js/codec.js +123 -0
  38. package/js/host.js +8 -0
  39. package/js/lean-client.js +147 -0
  40. package/js/lean-host.js +237 -0
  41. package/js/page.js +79 -0
  42. package/js/polyfills.js +32 -0
  43. package/package.json +30 -6
  44. package/third_party/jsi/LICENSE +21 -0
  45. package/third_party/jsi/README.md +18 -0
  46. package/third_party/jsi/include/jsi/hermes-interfaces.h +189 -0
  47. package/third_party/jsi/include/jsi/instrumentation.h +132 -0
  48. package/third_party/jsi/include/jsi/jsi-inl.h +434 -0
  49. package/third_party/jsi/include/jsi/jsi.h +2266 -0
  50. package/third_party/jsi/jsi.cpp +1110 -0
  51. package/third_party/webview/LICENSE +22 -0
  52. package/third_party/webview/README.md +24 -0
  53. package/third_party/webview/core/include/webview/api.h +251 -0
  54. package/third_party/webview/core/include/webview/backends.hh +40 -0
  55. package/third_party/webview/core/include/webview/c_api_impl.hh +258 -0
  56. package/third_party/webview/core/include/webview/detail/backends/cocoa_webkit.hh +633 -0
  57. package/third_party/webview/core/include/webview/detail/backends/gtk_webkitgtk.hh +355 -0
  58. package/third_party/webview/core/include/webview/detail/backends/win32_edge.hh +915 -0
  59. package/third_party/webview/core/include/webview/detail/basic_result.hh +119 -0
  60. package/third_party/webview/core/include/webview/detail/engine_base.hh +382 -0
  61. package/third_party/webview/core/include/webview/detail/exceptions.hh +42 -0
  62. package/third_party/webview/core/include/webview/detail/json.hh +337 -0
  63. package/third_party/webview/core/include/webview/detail/native_library.hh +176 -0
  64. package/third_party/webview/core/include/webview/detail/optional.hh +118 -0
  65. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSApplication.hh +107 -0
  66. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSBundle.hh +56 -0
  67. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSEvent.hh +71 -0
  68. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSInvocation.hh +66 -0
  69. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSMethodSignature.hh +52 -0
  70. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSNotification.hh +51 -0
  71. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSNumber.hh +53 -0
  72. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSObject.hh +51 -0
  73. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSOpenPanel.hh +71 -0
  74. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSPoint.hh +53 -0
  75. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSRect.hh +53 -0
  76. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSSavePanel.hh +52 -0
  77. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSSize.hh +53 -0
  78. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSString.hh +87 -0
  79. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSURL.hh +63 -0
  80. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSURLRequest.hh +52 -0
  81. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSValue.hh +56 -0
  82. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSView.hh +91 -0
  83. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/NSWindow.hh +137 -0
  84. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/cocoa.hh +59 -0
  85. package/third_party/webview/core/include/webview/detail/platform/darwin/cocoa/types.hh +49 -0
  86. package/third_party/webview/core/include/webview/detail/platform/darwin/objc/Class.hh +55 -0
  87. package/third_party/webview/core/include/webview/detail/platform/darwin/objc/autoreleasepool.hh +70 -0
  88. package/third_party/webview/core/include/webview/detail/platform/darwin/objc/invoke.hh +75 -0
  89. package/third_party/webview/core/include/webview/detail/platform/darwin/objc/memory.hh +57 -0
  90. package/third_party/webview/core/include/webview/detail/platform/darwin/objc/objc.hh +47 -0
  91. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKOpenPanelParameters.hh +57 -0
  92. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKScriptMessage.hh +51 -0
  93. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKUserContentController.hh +61 -0
  94. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKUserScript.hh +71 -0
  95. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKWebView.hh +108 -0
  96. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/WKWebViewConfiguration.hh +60 -0
  97. package/third_party/webview/core/include/webview/detail/platform/darwin/webkit/webkit.hh +46 -0
  98. package/third_party/webview/core/include/webview/detail/platform/linux/gtk/compat.hh +137 -0
  99. package/third_party/webview/core/include/webview/detail/platform/linux/webkitgtk/compat.hh +142 -0
  100. package/third_party/webview/core/include/webview/detail/platform/linux/webkitgtk/dmabuf.hh +167 -0
  101. package/third_party/webview/core/include/webview/detail/platform/windows/com_init_wrapper.hh +126 -0
  102. package/third_party/webview/core/include/webview/detail/platform/windows/dpi.hh +161 -0
  103. package/third_party/webview/core/include/webview/detail/platform/windows/dwmapi.hh +67 -0
  104. package/third_party/webview/core/include/webview/detail/platform/windows/iid.hh +78 -0
  105. package/third_party/webview/core/include/webview/detail/platform/windows/ntdll.hh +58 -0
  106. package/third_party/webview/core/include/webview/detail/platform/windows/reg_key.hh +135 -0
  107. package/third_party/webview/core/include/webview/detail/platform/windows/shcore.hh +59 -0
  108. package/third_party/webview/core/include/webview/detail/platform/windows/theme.hh +76 -0
  109. package/third_party/webview/core/include/webview/detail/platform/windows/user32.hh +90 -0
  110. package/third_party/webview/core/include/webview/detail/platform/windows/version.hh +151 -0
  111. package/third_party/webview/core/include/webview/detail/platform/windows/webview2/loader.hh +391 -0
  112. package/third_party/webview/core/include/webview/detail/user_script.hh +76 -0
  113. package/third_party/webview/core/include/webview/detail/utility/string.hh +99 -0
  114. package/third_party/webview/core/include/webview/errors.h +67 -0
  115. package/third_party/webview/core/include/webview/errors.hh +85 -0
  116. package/third_party/webview/core/include/webview/json_deprecated.hh +61 -0
  117. package/third_party/webview/core/include/webview/macros.h +123 -0
  118. package/third_party/webview/core/include/webview/types.h +81 -0
  119. package/third_party/webview/core/include/webview/types.hh +48 -0
  120. package/third_party/webview/core/include/webview/version.h +67 -0
  121. package/third_party/webview/core/include/webview/webview.h +32 -0
  122. package/third_party/webview/core/include/webview.h +15 -0
  123. package/third_party/webview2/LICENSE +27 -0
  124. package/third_party/webview2/README.md +13 -0
@@ -0,0 +1,40 @@
1
+ #include "app.h"
2
+
3
+ #include <cstdio>
4
+ #include <cstdlib>
5
+
6
+ namespace crossbind::app {
7
+
8
+ const char *devUrl() {
9
+ #ifdef CROSSBIND_APP_DEV
10
+ return std::getenv("CROSSBIND_APP_URL");
11
+ #else
12
+ // A release build loads its own page, whatever starts it.
13
+ return nullptr;
14
+ #endif
15
+ }
16
+
17
+ Core &start(const char *devUrl) {
18
+ const std::string resources = resourceDir();
19
+ Core &core = *new Core(resources + "/page");
20
+ core.cors = devUrl != nullptr;
21
+ std::string bootError;
22
+ try {
23
+ bootError = core.boot(readFile(resources + "/host.hbc"), "host.hbc", resources + "/data");
24
+ } catch (const std::exception &error) {
25
+ bootError = error.what();
26
+ }
27
+ if (!bootError.empty()) {
28
+ std::fprintf(stderr, "crossbind: the app's module failed to start: %s\n", bootError.c_str());
29
+ quit(core, 1);
30
+ }
31
+ return core;
32
+ }
33
+
34
+ void quit(Core &core, int status) {
35
+ core.stop();
36
+ std::fflush(nullptr);
37
+ std::_Exit(status);
38
+ }
39
+
40
+ } // namespace crossbind::app
package/cpp/src/app.h ADDED
@@ -0,0 +1,21 @@
1
+ #pragma once
2
+
3
+ // What an app does around its webview, whatever makes its window: the module starts from the app's files, and the
4
+ // webview gets the page's token, the module's channel, the page's console and the navigation lock.
5
+ #include "host_core.h"
6
+
7
+ namespace crossbind::app {
8
+
9
+ // The page a debug app opens instead of its own: the dev server's, which CROSSBIND_APP_URL names. Null in a release app.
10
+ const char *devUrl();
11
+ // Starts the module from the app's files, or ends the app with the reason.
12
+ Core &start(const char *devUrl);
13
+ // Ends the app without running destructors: bindings keep JSI values in globals, which must not be destroyed after the
14
+ // runtime they belong to.
15
+ [[noreturn]] void quit(Core &core, int status);
16
+ // Readies a webview for the page, and returns the URL it opens first. An Android app's activity does this in Java.
17
+ std::string attach(Core &core, void *webView, const char *devUrl);
18
+ // The scheme and authority of a URL: the origin a webview names a page by.
19
+ std::string originOf(const std::string &url);
20
+
21
+ } // namespace crossbind::app
@@ -0,0 +1,40 @@
1
+ #include "app.h"
2
+
3
+ #include <algorithm>
4
+ #include <cstdlib>
5
+
6
+ namespace crossbind::app {
7
+
8
+ namespace {
9
+
10
+ constexpr const char *APP_ORIGIN = "crossbind://app";
11
+ constexpr const char *APP_URL = "crossbind://app/";
12
+ constexpr const char *REFUSED = "[1,{\"name\":\"Error\",\"message\":\"crossbind: a page from this origin may not call the app's module\"}]";
13
+
14
+ } // namespace
15
+
16
+ std::string originOf(const std::string &url) {
17
+ const auto scheme = url.find("://");
18
+ if (scheme == std::string::npos) return url;
19
+ return url.substr(0, url.find_first_of("/?#", scheme + 3));
20
+ }
21
+
22
+ std::string attach(Core &core, void *webView, const char *devUrl) {
23
+ std::vector<std::string> origins{APP_ORIGIN};
24
+ if (devUrl) origins.push_back(originOf(devUrl));
25
+ // Only the top frame gets the token every message and crossbind:// call carries: a frame of another origin, or a
26
+ // sandboxed one, cannot read it.
27
+ addTopFrameScript(webView, "Object.defineProperty(globalThis, '__crossbind_token', { value: '" + core.token() + "' });");
28
+ installReplyChannel(webView, "crossbind", [&core, origins](std::string origin, std::string message, ReplyFn reply) {
29
+ if (!origin.empty() && std::find(origins.begin(), origins.end(), origin) == origins.end()) {
30
+ reply(REFUSED);
31
+ return;
32
+ }
33
+ core.receive(std::move(message), std::move(reply));
34
+ });
35
+ if (devUrl || std::getenv("CROSSBIND_APP_CONSOLE")) forwardConsole(webView);
36
+ restrictNavigation(webView, origins, [&core] { core.reset(); });
37
+ return devUrl ? devUrl : APP_URL;
38
+ }
39
+
40
+ } // namespace crossbind::app
@@ -0,0 +1,19 @@
1
+ #import <Cocoa/Cocoa.h>
2
+
3
+ #include "platform.h"
4
+
5
+ std::string resourceDir() { return NSBundle.mainBundle.resourcePath.UTF8String; }
6
+
7
+ std::string appName() {
8
+ NSString *name = [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleName"] ?: NSProcessInfo.processInfo.processName;
9
+ return name.UTF8String;
10
+ }
11
+
12
+ void onQuit(std::function<void()> quit) {
13
+ [NSNotificationCenter.defaultCenter addObserverForName:NSApplicationWillTerminateNotification
14
+ object:nil
15
+ queue:nil
16
+ usingBlock:^(NSNotification *) {
17
+ quit();
18
+ }];
19
+ }
@@ -0,0 +1,196 @@
1
+ #import <WebKit/WebKit.h>
2
+
3
+ #include "platform.h"
4
+
5
+ #include <cstdio>
6
+ #include <memory>
7
+
8
+ namespace {
9
+
10
+ // Wraps the page's console and reports uncaught errors, in the main frame alone.
11
+ NSString *const CONSOLE_SCRIPT = @"(() => {"
12
+ "const text = (args) => args.map((arg) => {"
13
+ " if (arg instanceof Error) return `${arg.message}\\n${arg.stack}`;"
14
+ " if (typeof arg === 'string') return arg;"
15
+ " try { return JSON.stringify(arg); } catch { return String(arg); }"
16
+ "}).join(' ');"
17
+ "const post = (level, args) => window.webkit.messageHandlers.crossbind_console.postMessage([level, text(args)]);"
18
+ "for (const level of ['log', 'info', 'warn', 'error', 'debug']) {"
19
+ " const original = console[level].bind(console);"
20
+ " console[level] = (...args) => { post(level, args); original(...args); };"
21
+ "}"
22
+ "addEventListener('error', (event) => post('error', [event.error ?? event.message]));"
23
+ "addEventListener('unhandledrejection', (event) => post('error', [event.reason]));"
24
+ "})();";
25
+
26
+ void onMainThread(void (^block)(void)) {
27
+ if ([NSThread isMainThread]) {
28
+ block();
29
+ } else {
30
+ dispatch_async(dispatch_get_main_queue(), block);
31
+ }
32
+ }
33
+
34
+ } // namespace
35
+
36
+ @interface CBReplyChannel : NSObject <WKScriptMessageHandlerWithReply>
37
+ - (instancetype)initWithHandler:(MessageHandler)handler;
38
+ @end
39
+
40
+ @implementation CBReplyChannel {
41
+ MessageHandler _handler;
42
+ }
43
+
44
+ - (instancetype)initWithHandler:(MessageHandler)handler {
45
+ if ((self = [super init])) _handler = std::move(handler);
46
+ return self;
47
+ }
48
+
49
+ - (void)userContentController:(WKUserContentController *)controller
50
+ didReceiveScriptMessage:(WKScriptMessage *)message
51
+ replyHandler:(void (^)(id, NSString *))replyHandler {
52
+ NSString *body = [message.body isKindOfClass:[NSString class]] ? message.body : @"";
53
+ WKSecurityOrigin *sender = message.frameInfo.securityOrigin;
54
+ NSString *origin = [NSString stringWithFormat:@"%@://%@%@", sender.protocol, sender.host,
55
+ sender.port ? [NSString stringWithFormat:@":%ld", static_cast<long>(sender.port)] : @""];
56
+ void (^reply)(id, NSString *) = [replyHandler copy];
57
+ _handler(std::string(origin.UTF8String ?: ""), std::string(body.UTF8String ?: ""), [reply](const std::string &text) {
58
+ // The host answers from its JS thread, which has no autorelease pool of its own.
59
+ @autoreleasepool {
60
+ NSString *answer = [[NSString alloc] initWithBytes:text.data() length:text.size() encoding:NSUTF8StringEncoding];
61
+ onMainThread(^{
62
+ reply(answer, nil);
63
+ });
64
+ }
65
+ });
66
+ }
67
+
68
+ @end
69
+
70
+ @interface CBConsole : NSObject <WKScriptMessageHandler>
71
+ @end
72
+
73
+ @implementation CBConsole
74
+
75
+ - (void)userContentController:(WKUserContentController *)controller didReceiveScriptMessage:(WKScriptMessage *)message {
76
+ NSArray *entry = [message.body isKindOfClass:[NSArray class]] ? message.body : nil;
77
+ if (entry.count != 2) return;
78
+ NSString *level = [entry[0] description];
79
+ std::FILE *out = [level isEqualToString:@"warn"] || [level isEqualToString:@"error"] ? stderr : stdout;
80
+ std::fprintf(out, "%s\n", [entry[1] description].UTF8String);
81
+ std::fflush(out);
82
+ }
83
+
84
+ @end
85
+
86
+ @interface CBSchemeHandler : NSObject <WKURLSchemeHandler>
87
+ - (instancetype)initWithHandler:(SchemeHandler)handler;
88
+ @end
89
+
90
+ @implementation CBSchemeHandler {
91
+ SchemeHandler _handler;
92
+ NSMutableSet *_stopped;
93
+ }
94
+
95
+ - (instancetype)initWithHandler:(SchemeHandler)handler {
96
+ if ((self = [super init])) {
97
+ _handler = std::move(handler);
98
+ _stopped = [NSMutableSet set];
99
+ }
100
+ return self;
101
+ }
102
+
103
+ static std::vector<uint8_t> readBody(NSURLRequest *request) {
104
+ std::vector<uint8_t> body;
105
+ if (NSData *data = request.HTTPBody) {
106
+ body.assign(static_cast<const uint8_t *>(data.bytes), static_cast<const uint8_t *>(data.bytes) + data.length);
107
+ return body;
108
+ }
109
+ NSInputStream *stream = request.HTTPBodyStream;
110
+ if (!stream) return body;
111
+ [stream open];
112
+ uint8_t chunk[65536];
113
+ for (NSInteger read; (read = [stream read:chunk maxLength:sizeof chunk]) > 0;) {
114
+ body.insert(body.end(), chunk, chunk + read);
115
+ }
116
+ [stream close];
117
+ return body;
118
+ }
119
+
120
+ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)task {
121
+ NSURLRequest *request = task.request;
122
+ SchemeRequest incoming;
123
+ incoming.method = request.HTTPMethod.UTF8String ?: "GET";
124
+ incoming.path = request.URL.path.UTF8String ?: "";
125
+ incoming.query = request.URL.query.UTF8String ?: "";
126
+ incoming.body = readBody(request);
127
+
128
+ id<WKURLSchemeTask> held = task;
129
+ NSURL *url = request.URL;
130
+ NSMutableSet *stopped = _stopped;
131
+ _handler(std::move(incoming), [held, url, stopped](SchemeResponse response) {
132
+ // A call's answer comes from the host's JS thread, which has no autorelease pool of its own.
133
+ @autoreleasepool {
134
+ auto *bytes = new std::vector<uint8_t>(std::move(response.body));
135
+ NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes->data()
136
+ length:bytes->size()
137
+ deallocator:^(void *, NSUInteger) { delete bytes; }];
138
+ NSMutableDictionary *headers = [@{
139
+ @"Content-Type": [NSString stringWithUTF8String:response.mime.c_str()],
140
+ @"Content-Length": [NSString stringWithFormat:@"%lu", static_cast<unsigned long>(data.length)],
141
+ } mutableCopy];
142
+ for (const auto &[name, value] : response.headers) {
143
+ headers[[NSString stringWithUTF8String:name.c_str()]] = [NSString stringWithUTF8String:value.c_str()];
144
+ }
145
+ NSHTTPURLResponse *http = [[NSHTTPURLResponse alloc] initWithURL:url
146
+ statusCode:response.status
147
+ HTTPVersion:@"HTTP/1.1"
148
+ headerFields:headers];
149
+ onMainThread(^{
150
+ // The page may have dropped the request meanwhile; WebKit refuses answers to a stopped task.
151
+ if ([stopped containsObject:held]) {
152
+ [stopped removeObject:held];
153
+ return;
154
+ }
155
+ [held didReceiveResponse:http];
156
+ [held didReceiveData:data];
157
+ [held didFinish];
158
+ });
159
+ }
160
+ });
161
+ }
162
+
163
+ - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)task {
164
+ [_stopped addObject:task];
165
+ }
166
+
167
+ @end
168
+
169
+ void installReplyChannel(void *webView, const char *name, MessageHandler handler) {
170
+ WKWebView *view = (__bridge WKWebView *)webView;
171
+ CBReplyChannel *channel = [[CBReplyChannel alloc] initWithHandler:std::move(handler)];
172
+ [view.configuration.userContentController addScriptMessageHandlerWithReply:channel
173
+ contentWorld:WKContentWorld.pageWorld
174
+ name:[NSString stringWithUTF8String:name]];
175
+ }
176
+
177
+ void forwardConsole(void *webView) {
178
+ WKUserContentController *controller = ((__bridge WKWebView *)webView).configuration.userContentController;
179
+ [controller addScriptMessageHandler:[CBConsole new] name:@"crossbind_console"];
180
+ [controller addUserScript:[[WKUserScript alloc] initWithSource:CONSOLE_SCRIPT
181
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
182
+ forMainFrameOnly:YES]];
183
+ }
184
+
185
+ void addTopFrameScript(void *webView, const std::string &source) {
186
+ WKUserContentController *controller = ((__bridge WKWebView *)webView).configuration.userContentController;
187
+ [controller addUserScript:[[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:source.c_str()]
188
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart
189
+ forMainFrameOnly:YES]];
190
+ }
191
+
192
+ void registerSchemeHandler(void *target, const char *scheme, SchemeHandler handler) {
193
+ WKWebViewConfiguration *config = (__bridge WKWebViewConfiguration *)target;
194
+ CBSchemeHandler *schemeHandler = [[CBSchemeHandler alloc] initWithHandler:std::move(handler)];
195
+ [config setURLSchemeHandler:schemeHandler forURLScheme:[NSString stringWithUTF8String:scheme]];
196
+ }
@@ -0,0 +1,69 @@
1
+ #import <TargetConditionals.h>
2
+ #if TARGET_OS_IPHONE
3
+ #import <UIKit/UIKit.h>
4
+ #else
5
+ #import <Cocoa/Cocoa.h>
6
+ #endif
7
+ #import <WebKit/WebKit.h>
8
+ #import <objc/runtime.h>
9
+
10
+ #include "platform.h"
11
+
12
+ #include <algorithm>
13
+
14
+ // A page from another origin in any frame would reach the module's channel and its crossbind:// routes, so the webview
15
+ // never loads one. A link the user follows to such a page opens in the default browser instead.
16
+ @interface CBNavigationLock : NSObject <WKNavigationDelegate> {
17
+ @public
18
+ std::vector<std::string> origins;
19
+ std::function<void()> newPage;
20
+ }
21
+ @end
22
+
23
+ @implementation CBNavigationLock
24
+
25
+ - (BOOL)allows:(NSURL *)url {
26
+ NSString *scheme = url.scheme.lowercaseString;
27
+ if ([scheme isEqualToString:@"about"]) return YES;
28
+ // A blob URL carries the origin of the page that made it.
29
+ if ([scheme isEqualToString:@"blob"]) return [self allows:[NSURL URLWithString:url.resourceSpecifier]];
30
+ std::string origin = std::string(scheme.UTF8String ?: "") + "://" + (url.host.UTF8String ?: "");
31
+ if (url.port) origin += ":" + std::to_string(url.port.intValue);
32
+ return std::find(origins.begin(), origins.end(), origin) != origins.end();
33
+ }
34
+
35
+ - (void)webView:(WKWebView *)webView
36
+ decidePolicyForNavigationAction:(WKNavigationAction *)action
37
+ decisionHandler:(void (^)(WKNavigationActionPolicy))decide {
38
+ NSURL *url = action.request.URL;
39
+ if ([self allows:url]) {
40
+ decide(WKNavigationActionPolicyAllow);
41
+ return;
42
+ }
43
+ const BOOL followed = action.navigationType == WKNavigationTypeLinkActivated || action.targetFrame == nil;
44
+ if (followed && [@[ @"http", @"https", @"mailto" ] containsObject:url.scheme.lowercaseString]) {
45
+ #if TARGET_OS_IPHONE
46
+ [UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil];
47
+ #else
48
+ [NSWorkspace.sharedWorkspace openURL:url];
49
+ #endif
50
+ }
51
+ decide(WKNavigationActionPolicyCancel);
52
+ }
53
+
54
+ - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
55
+ newPage();
56
+ }
57
+
58
+ @end
59
+
60
+ void restrictNavigation(void *webView, std::vector<std::string> origins, std::function<void()> newPage) {
61
+ static char key;
62
+ WKWebView *view = (__bridge WKWebView *)webView;
63
+ CBNavigationLock *lock = [CBNavigationLock new];
64
+ lock->origins = std::move(origins);
65
+ lock->newPage = std::move(newPage);
66
+ // The webview holds its navigation delegate weakly.
67
+ objc_setAssociatedObject(view, &key, lock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
68
+ view.navigationDelegate = lock;
69
+ }