tauri-cocoa.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. #include "tauri-cocoa-webview.h"
  2. #include "tauri.h"
  3. #define NSAlertStyleWarning 0
  4. #define NSAlertStyleCritical 2
  5. #define NSWindowStyleMaskResizable 8
  6. #define NSWindowStyleMaskMiniaturizable 4
  7. #define NSWindowStyleMaskTitled 1
  8. #define NSWindowStyleMaskClosable 2
  9. #define NSWindowStyleMaskFullScreen (1 << 14)
  10. #define NSViewWidthSizable 2
  11. #define NSViewHeightSizable 16
  12. #define NSBackingStoreBuffered 2
  13. #define NSEventMaskAny ULONG_MAX
  14. #define NSEventModifierFlagCommand (1 << 20)
  15. #define NSEventModifierFlagOption (1 << 19)
  16. #define NSAlertStyleInformational 1
  17. #define NSAlertFirstButtonReturn 1000
  18. #define WKNavigationActionPolicyDownload 2
  19. #define NSModalResponseOK 1
  20. #define WKNavigationActionPolicyDownload 2
  21. #define WKNavigationResponsePolicyAllow 1
  22. #define WKUserScriptInjectionTimeAtDocumentStart 0
  23. #define NSApplicationActivationPolicyRegular 0
  24. static id get_nsstring(const char *c_str) {
  25. return objc_msgSend((id)objc_getClass("NSString"),
  26. sel_registerName("stringWithUTF8String:"), c_str);
  27. }
  28. static id create_menu_item(id title, const char *action, const char *key) {
  29. id item =
  30. objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
  31. objc_msgSend(item, sel_registerName("initWithTitle:action:keyEquivalent:"),
  32. title, sel_registerName(action), get_nsstring(key));
  33. objc_msgSend(item, sel_registerName("autorelease"));
  34. return item;
  35. }
  36. static void webview_window_will_close(id self, SEL cmd, id notification) {
  37. struct webview *w =
  38. (struct webview *)objc_getAssociatedObject(self, "webview");
  39. webview_terminate(w);
  40. }
  41. static void webview_external_invoke(id self, SEL cmd, id contentController,
  42. id message) {
  43. struct webview *w =
  44. (struct webview *)objc_getAssociatedObject(contentController, "webview");
  45. if (w == NULL || w->external_invoke_cb == NULL) {
  46. return;
  47. }
  48. w->external_invoke_cb(w, (const char *)objc_msgSend(
  49. objc_msgSend(message, sel_registerName("body")),
  50. sel_registerName("UTF8String")));
  51. }
  52. static void run_open_panel(id self, SEL cmd, id webView, id parameters,
  53. id frame, void (^completionHandler)(id)) {
  54. id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"),
  55. sel_registerName("openPanel"));
  56. objc_msgSend(
  57. openPanel, sel_registerName("setAllowsMultipleSelection:"),
  58. objc_msgSend(parameters, sel_registerName("allowsMultipleSelection")));
  59. objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1);
  60. objc_msgSend(
  61. openPanel, sel_registerName("beginWithCompletionHandler:"), ^(id result) {
  62. if (result == (id)NSModalResponseOK) {
  63. completionHandler(objc_msgSend(openPanel, sel_registerName("URLs")));
  64. } else {
  65. completionHandler(nil);
  66. }
  67. });
  68. }
  69. static void run_save_panel(id self, SEL cmd, id download, id filename,
  70. void (^completionHandler)(int allowOverwrite,
  71. id destination)) {
  72. id savePanel = objc_msgSend((id)objc_getClass("NSSavePanel"),
  73. sel_registerName("savePanel"));
  74. objc_msgSend(savePanel, sel_registerName("setCanCreateDirectories:"), 1);
  75. objc_msgSend(savePanel, sel_registerName("setNameFieldStringValue:"),
  76. filename);
  77. objc_msgSend(savePanel, sel_registerName("beginWithCompletionHandler:"),
  78. ^(id result) {
  79. if (result == (id)NSModalResponseOK) {
  80. id url = objc_msgSend(savePanel, sel_registerName("URL"));
  81. id path = objc_msgSend(url, sel_registerName("path"));
  82. completionHandler(1, path);
  83. } else {
  84. completionHandler(NO, nil);
  85. }
  86. });
  87. }
  88. static void run_confirmation_panel(id self, SEL cmd, id webView, id message,
  89. id frame, void (^completionHandler)(bool)) {
  90. id alert =
  91. objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
  92. objc_msgSend(alert, sel_registerName("setIcon:"),
  93. objc_msgSend((id)objc_getClass("NSImage"),
  94. sel_registerName("imageNamed:"),
  95. get_nsstring("NSCaution")));
  96. objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0);
  97. objc_msgSend(alert, sel_registerName("setInformativeText:"), message);
  98. objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
  99. get_nsstring("OK"));
  100. objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
  101. get_nsstring("Cancel"));
  102. if (objc_msgSend(alert, sel_registerName("runModal")) ==
  103. (id)NSAlertFirstButtonReturn) {
  104. completionHandler(true);
  105. } else {
  106. completionHandler(false);
  107. }
  108. objc_msgSend(alert, sel_registerName("release"));
  109. }
  110. static void run_alert_panel(id self, SEL cmd, id webView, id message, id frame,
  111. void (^completionHandler)(void)) {
  112. id alert =
  113. objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
  114. objc_msgSend(alert, sel_registerName("setIcon:"),
  115. objc_msgSend((id)objc_getClass("NSImage"),
  116. sel_registerName("imageNamed:"),
  117. get_nsstring("NSCaution")));
  118. objc_msgSend(alert, sel_registerName("setShowsHelp:"), 0);
  119. objc_msgSend(alert, sel_registerName("setInformativeText:"), message);
  120. objc_msgSend(alert, sel_registerName("addButtonWithTitle:"),
  121. get_nsstring("OK"));
  122. objc_msgSend(alert, sel_registerName("runModal"));
  123. objc_msgSend(alert, sel_registerName("release"));
  124. completionHandler();
  125. }
  126. static void download_failed(id self, SEL cmd, id download, id error) {
  127. printf("%s",
  128. (const char *)objc_msgSend(
  129. objc_msgSend(error, sel_registerName("localizedDescription")),
  130. sel_registerName("UTF8String")));
  131. }
  132. static void make_nav_policy_decision(id self, SEL cmd, id webView, id response,
  133. void (^decisionHandler)(int)) {
  134. if (objc_msgSend(response, sel_registerName("canShowMIMEType")) == 0) {
  135. decisionHandler(WKNavigationActionPolicyDownload);
  136. } else {
  137. decisionHandler(WKNavigationResponsePolicyAllow);
  138. }
  139. }
  140. WEBVIEW_API int webview_init(struct webview *w) {
  141. w->priv.pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"),
  142. sel_registerName("new"));
  143. objc_msgSend((id)objc_getClass("NSApplication"),
  144. sel_registerName("sharedApplication"));
  145. Class __WKScriptMessageHandler = objc_allocateClassPair(
  146. objc_getClass("NSObject"), "__WKScriptMessageHandler", 0);
  147. class_addMethod(
  148. __WKScriptMessageHandler,
  149. sel_registerName("userContentController:didReceiveScriptMessage:"),
  150. (IMP)webview_external_invoke, "v@:@@");
  151. objc_registerClassPair(__WKScriptMessageHandler);
  152. id scriptMessageHandler =
  153. objc_msgSend((id)__WKScriptMessageHandler, sel_registerName("new"));
  154. /***
  155. _WKDownloadDelegate is an undocumented/private protocol with methods called
  156. from WKNavigationDelegate
  157. References:
  158. https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h
  159. https://github.com/WebKit/webkit/blob/master/Source/WebKit/UIProcess/API/Cocoa/_WKDownloadDelegate.h
  160. https://github.com/WebKit/webkit/blob/master/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm
  161. ***/
  162. Class __WKDownloadDelegate = objc_allocateClassPair(
  163. objc_getClass("NSObject"), "__WKDownloadDelegate", 0);
  164. class_addMethod(
  165. __WKDownloadDelegate,
  166. sel_registerName("_download:decideDestinationWithSuggestedFilename:"
  167. "completionHandler:"),
  168. (IMP)run_save_panel, "v@:@@?");
  169. class_addMethod(__WKDownloadDelegate,
  170. sel_registerName("_download:didFailWithError:"),
  171. (IMP)download_failed, "v@:@@");
  172. objc_registerClassPair(__WKDownloadDelegate);
  173. id downloadDelegate =
  174. objc_msgSend((id)__WKDownloadDelegate, sel_registerName("new"));
  175. Class __WKPreferences = objc_allocateClassPair(objc_getClass("WKPreferences"),
  176. "__WKPreferences", 0);
  177. objc_property_attribute_t type = {"T", "c"};
  178. objc_property_attribute_t ownership = {"N", ""};
  179. objc_property_attribute_t attrs[] = {type, ownership};
  180. class_replaceProperty(__WKPreferences, "developerExtrasEnabled", attrs, 2);
  181. objc_registerClassPair(__WKPreferences);
  182. id wkPref = objc_msgSend((id)__WKPreferences, sel_registerName("new"));
  183. objc_msgSend(wkPref, sel_registerName("setValue:forKey:"),
  184. objc_msgSend((id)objc_getClass("NSNumber"),
  185. sel_registerName("numberWithBool:"), !!w->debug),
  186. objc_msgSend((id)objc_getClass("NSString"),
  187. sel_registerName("stringWithUTF8String:"),
  188. "developerExtrasEnabled"));
  189. id userController = objc_msgSend((id)objc_getClass("WKUserContentController"),
  190. sel_registerName("new"));
  191. objc_setAssociatedObject(userController, "webview", (id)(w),
  192. OBJC_ASSOCIATION_ASSIGN);
  193. objc_msgSend(
  194. userController, sel_registerName("addScriptMessageHandler:name:"),
  195. scriptMessageHandler,
  196. objc_msgSend((id)objc_getClass("NSString"),
  197. sel_registerName("stringWithUTF8String:"), "invoke"));
  198. /***
  199. In order to maintain compatibility with the other 'webviews' we need to
  200. override window.external.invoke to call
  201. webkit.messageHandlers.invoke.postMessage
  202. ***/
  203. id windowExternalOverrideScript = objc_msgSend(
  204. (id)objc_getClass("WKUserScript"), sel_registerName("alloc"));
  205. objc_msgSend(
  206. windowExternalOverrideScript,
  207. sel_registerName("initWithSource:injectionTime:forMainFrameOnly:"),
  208. get_nsstring("window.external = this; invoke = function(arg){ "
  209. "webkit.messageHandlers.invoke.postMessage(arg); };"),
  210. WKUserScriptInjectionTimeAtDocumentStart, 0);
  211. objc_msgSend(userController, sel_registerName("addUserScript:"),
  212. windowExternalOverrideScript);
  213. id config = objc_msgSend((id)objc_getClass("WKWebViewConfiguration"),
  214. sel_registerName("new"));
  215. id processPool = objc_msgSend(config, sel_registerName("processPool"));
  216. objc_msgSend(processPool, sel_registerName("_setDownloadDelegate:"),
  217. downloadDelegate);
  218. objc_msgSend(config, sel_registerName("setProcessPool:"), processPool);
  219. objc_msgSend(config, sel_registerName("setUserContentController:"),
  220. userController);
  221. objc_msgSend(config, sel_registerName("setPreferences:"), wkPref);
  222. Class __NSWindowDelegate = objc_allocateClassPair(objc_getClass("NSObject"),
  223. "__NSWindowDelegate", 0);
  224. class_addProtocol(__NSWindowDelegate, objc_getProtocol("NSWindowDelegate"));
  225. class_replaceMethod(__NSWindowDelegate, sel_registerName("windowWillClose:"),
  226. (IMP)webview_window_will_close, "v@:@");
  227. objc_registerClassPair(__NSWindowDelegate);
  228. w->priv.windowDelegate =
  229. objc_msgSend((id)__NSWindowDelegate, sel_registerName("new"));
  230. objc_setAssociatedObject(w->priv.windowDelegate, "webview", (id)(w),
  231. OBJC_ASSOCIATION_ASSIGN);
  232. id nsTitle =
  233. objc_msgSend((id)objc_getClass("NSString"),
  234. sel_registerName("stringWithUTF8String:"), w->title);
  235. CGRect r = CGRectMake(0, 0, w->width, w->height);
  236. unsigned int style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
  237. NSWindowStyleMaskMiniaturizable;
  238. if (w->resizable) {
  239. style = style | NSWindowStyleMaskResizable;
  240. }
  241. w->priv.window =
  242. objc_msgSend((id)objc_getClass("NSWindow"), sel_registerName("alloc"));
  243. objc_msgSend(w->priv.window,
  244. sel_registerName("initWithContentRect:styleMask:backing:defer:"),
  245. r, style, NSBackingStoreBuffered, 0);
  246. objc_msgSend(w->priv.window, sel_registerName("autorelease"));
  247. objc_msgSend(w->priv.window, sel_registerName("setTitle:"), nsTitle);
  248. objc_msgSend(w->priv.window, sel_registerName("setDelegate:"),
  249. w->priv.windowDelegate);
  250. objc_msgSend(w->priv.window, sel_registerName("center"));
  251. Class __WKUIDelegate =
  252. objc_allocateClassPair(objc_getClass("NSObject"), "__WKUIDelegate", 0);
  253. class_addProtocol(__WKUIDelegate, objc_getProtocol("WKUIDelegate"));
  254. class_addMethod(__WKUIDelegate,
  255. sel_registerName("webView:runOpenPanelWithParameters:"
  256. "initiatedByFrame:completionHandler:"),
  257. (IMP)run_open_panel, "v@:@@@?");
  258. class_addMethod(__WKUIDelegate,
  259. sel_registerName("webView:runJavaScriptAlertPanelWithMessage:"
  260. "initiatedByFrame:completionHandler:"),
  261. (IMP)run_alert_panel, "v@:@@@?");
  262. class_addMethod(
  263. __WKUIDelegate,
  264. sel_registerName("webView:runJavaScriptConfirmPanelWithMessage:"
  265. "initiatedByFrame:completionHandler:"),
  266. (IMP)run_confirmation_panel, "v@:@@@?");
  267. objc_registerClassPair(__WKUIDelegate);
  268. id uiDel = objc_msgSend((id)__WKUIDelegate, sel_registerName("new"));
  269. Class __WKNavigationDelegate = objc_allocateClassPair(
  270. objc_getClass("NSObject"), "__WKNavigationDelegate", 0);
  271. class_addProtocol(__WKNavigationDelegate,
  272. objc_getProtocol("WKNavigationDelegate"));
  273. class_addMethod(
  274. __WKNavigationDelegate,
  275. sel_registerName(
  276. "webView:decidePolicyForNavigationResponse:decisionHandler:"),
  277. (IMP)make_nav_policy_decision, "v@:@@?");
  278. objc_registerClassPair(__WKNavigationDelegate);
  279. id navDel = objc_msgSend((id)__WKNavigationDelegate, sel_registerName("new"));
  280. w->priv.webview =
  281. objc_msgSend((id)objc_getClass("WKWebView"), sel_registerName("alloc"));
  282. objc_msgSend(w->priv.webview,
  283. sel_registerName("initWithFrame:configuration:"), r, config);
  284. objc_msgSend(w->priv.webview, sel_registerName("setUIDelegate:"), uiDel);
  285. objc_msgSend(w->priv.webview, sel_registerName("setNavigationDelegate:"),
  286. navDel);
  287. id nsURL = objc_msgSend((id)objc_getClass("NSURL"),
  288. sel_registerName("URLWithString:"),
  289. get_nsstring(webview_check_url(w->url)));
  290. objc_msgSend(w->priv.webview, sel_registerName("loadRequest:"),
  291. objc_msgSend((id)objc_getClass("NSURLRequest"),
  292. sel_registerName("requestWithURL:"), nsURL));
  293. objc_msgSend(w->priv.webview, sel_registerName("setAutoresizesSubviews:"), 1);
  294. objc_msgSend(w->priv.webview, sel_registerName("setAutoresizingMask:"),
  295. (NSViewWidthSizable | NSViewHeightSizable));
  296. objc_msgSend(objc_msgSend(w->priv.window, sel_registerName("contentView")),
  297. sel_registerName("addSubview:"), w->priv.webview);
  298. objc_msgSend(w->priv.window, sel_registerName("orderFrontRegardless"));
  299. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  300. sel_registerName("sharedApplication")),
  301. sel_registerName("setActivationPolicy:"),
  302. NSApplicationActivationPolicyRegular);
  303. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  304. sel_registerName("sharedApplication")),
  305. sel_registerName("finishLaunching"));
  306. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  307. sel_registerName("sharedApplication")),
  308. sel_registerName("activateIgnoringOtherApps:"), 1);
  309. id menubar =
  310. objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
  311. objc_msgSend(menubar, sel_registerName("initWithTitle:"), get_nsstring(""));
  312. objc_msgSend(menubar, sel_registerName("autorelease"));
  313. id appName = objc_msgSend(objc_msgSend((id)objc_getClass("NSProcessInfo"),
  314. sel_registerName("processInfo")),
  315. sel_registerName("processName"));
  316. id appMenuItem =
  317. objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
  318. objc_msgSend(appMenuItem,
  319. sel_registerName("initWithTitle:action:keyEquivalent:"), appName,
  320. NULL, get_nsstring(""));
  321. id appMenu =
  322. objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
  323. objc_msgSend(appMenu, sel_registerName("initWithTitle:"), appName);
  324. objc_msgSend(appMenu, sel_registerName("autorelease"));
  325. objc_msgSend(appMenuItem, sel_registerName("setSubmenu:"), appMenu);
  326. objc_msgSend(menubar, sel_registerName("addItem:"), appMenuItem);
  327. id title =
  328. objc_msgSend(get_nsstring("Hide "),
  329. sel_registerName("stringByAppendingString:"), appName);
  330. id item = create_menu_item(title, "hide:", "h");
  331. objc_msgSend(appMenu, sel_registerName("addItem:"), item);
  332. item = create_menu_item(get_nsstring("Hide Others"),
  333. "hideOtherApplications:", "h");
  334. objc_msgSend(item, sel_registerName("setKeyEquivalentModifierMask:"),
  335. (NSEventModifierFlagOption | NSEventModifierFlagCommand));
  336. objc_msgSend(appMenu, sel_registerName("addItem:"), item);
  337. item =
  338. create_menu_item(get_nsstring("Show All"), "unhideAllApplications:", "");
  339. objc_msgSend(appMenu, sel_registerName("addItem:"), item);
  340. objc_msgSend(appMenu, sel_registerName("addItem:"),
  341. objc_msgSend((id)objc_getClass("NSMenuItem"),
  342. sel_registerName("separatorItem")));
  343. title = objc_msgSend(get_nsstring("Quit "),
  344. sel_registerName("stringByAppendingString:"), appName);
  345. item = create_menu_item(title, "terminate:", "q");
  346. objc_msgSend(appMenu, sel_registerName("addItem:"), item);
  347. id editMenuItem =
  348. objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
  349. objc_msgSend(editMenuItem,
  350. sel_registerName("initWithTitle:action:keyEquivalent:"), get_nsstring("Edit"),
  351. NULL, get_nsstring(""));
  352. /***
  353. Edit menu
  354. ***/
  355. id editMenu =
  356. objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("alloc"));
  357. objc_msgSend(editMenu, sel_registerName("initWithTitle:"), get_nsstring("Edit"));
  358. objc_msgSend(editMenu, sel_registerName("autorelease"));
  359. objc_msgSend(editMenuItem, sel_registerName("setSubmenu:"), editMenu);
  360. objc_msgSend(menubar, sel_registerName("addItem:"), editMenuItem);
  361. item = create_menu_item(get_nsstring("Undo"), "undo:", "z");
  362. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  363. item = create_menu_item(get_nsstring("Redo"), "redo:", "y");
  364. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  365. item = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem"));
  366. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  367. item = create_menu_item(get_nsstring("Cut"), "cut:", "x");
  368. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  369. item = create_menu_item(get_nsstring("Copy"), "copy:", "c");
  370. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  371. item = create_menu_item(get_nsstring("Paste"), "paste:", "v");
  372. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  373. item = create_menu_item(get_nsstring("Select All"), "selectAll:", "a");
  374. objc_msgSend(editMenu, sel_registerName("addItem:"), item);
  375. /***
  376. Finalize menubar
  377. ***/
  378. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  379. sel_registerName("sharedApplication")),
  380. sel_registerName("setMainMenu:"), menubar);
  381. w->priv.should_exit = 0;
  382. return 0;
  383. }
  384. WEBVIEW_API int webview_loop(struct webview *w, int blocking) {
  385. id until = (blocking ? objc_msgSend((id)objc_getClass("NSDate"),
  386. sel_registerName("distantFuture"))
  387. : objc_msgSend((id)objc_getClass("NSDate"),
  388. sel_registerName("distantPast")));
  389. id event = objc_msgSend(
  390. objc_msgSend((id)objc_getClass("NSApplication"),
  391. sel_registerName("sharedApplication")),
  392. sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
  393. ULONG_MAX, until,
  394. objc_msgSend((id)objc_getClass("NSString"),
  395. sel_registerName("stringWithUTF8String:"),
  396. "kCFRunLoopDefaultMode"),
  397. true);
  398. if (event) {
  399. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  400. sel_registerName("sharedApplication")),
  401. sel_registerName("sendEvent:"), event);
  402. }
  403. return w->priv.should_exit;
  404. }
  405. WEBVIEW_API int webview_eval(struct webview *w, const char *js) {
  406. objc_msgSend(w->priv.webview,
  407. sel_registerName("evaluateJavaScript:completionHandler:"),
  408. get_nsstring(js), NULL);
  409. return 0;
  410. }
  411. WEBVIEW_API void webview_set_title(struct webview *w, const char *title) {
  412. objc_msgSend(w->priv.window, sel_registerName("setTitle"),
  413. get_nsstring(title));
  414. }
  415. WEBVIEW_API void webview_set_fullscreen(struct webview *w, int fullscreen) {
  416. unsigned long windowStyleMask = (unsigned long)objc_msgSend(
  417. w->priv.window, sel_registerName("styleMask"));
  418. int b = (((windowStyleMask & NSWindowStyleMaskFullScreen) ==
  419. NSWindowStyleMaskFullScreen)
  420. ? 1
  421. : 0);
  422. if (b != fullscreen) {
  423. objc_msgSend(w->priv.window, sel_registerName("toggleFullScreen:"), NULL);
  424. }
  425. }
  426. WEBVIEW_API void webview_set_color(struct webview *w, uint8_t r, uint8_t g,
  427. uint8_t b, uint8_t a) {
  428. id color = objc_msgSend((id)objc_getClass("NSColor"),
  429. sel_registerName("colorWithRed:green:blue:alpha:"),
  430. (float)r / 255.0, (float)g / 255.0, (float)b / 255.0,
  431. (float)a / 255.0);
  432. objc_msgSend(w->priv.window, sel_registerName("setBackgroundColor:"), color);
  433. if (0.5 >= ((r / 255.0 * 299.0) + (g / 255.0 * 587.0) + (b / 255.0 * 114.0)) /
  434. 1000.0) {
  435. objc_msgSend(w->priv.window, sel_registerName("setAppearance:"),
  436. objc_msgSend((id)objc_getClass("NSAppearance"),
  437. sel_registerName("appearanceNamed:"),
  438. get_nsstring("NSAppearanceNameVibrantDark")));
  439. } else {
  440. objc_msgSend(w->priv.window, sel_registerName("setAppearance:"),
  441. objc_msgSend((id)objc_getClass("NSAppearance"),
  442. sel_registerName("appearanceNamed:"),
  443. get_nsstring("NSAppearanceNameVibrantLight")));
  444. }
  445. objc_msgSend(w->priv.window, sel_registerName("setOpaque:"), 0);
  446. objc_msgSend(w->priv.window,
  447. sel_registerName("setTitlebarAppearsTransparent:"), 1);
  448. objc_msgSend(w->priv.webview, sel_registerName("_setDrawsBackground:"), 0);
  449. }
  450. WEBVIEW_API void webview_dialog(struct webview *w,
  451. enum webview_dialog_type dlgtype, int flags,
  452. const char *title, const char *arg,
  453. char *result, size_t resultsz) {
  454. if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN ||
  455. dlgtype == WEBVIEW_DIALOG_TYPE_SAVE) {
  456. id panel = (id)objc_getClass("NSSavePanel");
  457. if (dlgtype == WEBVIEW_DIALOG_TYPE_OPEN) {
  458. id openPanel = objc_msgSend((id)objc_getClass("NSOpenPanel"),
  459. sel_registerName("openPanel"));
  460. if (flags & WEBVIEW_DIALOG_FLAG_DIRECTORY) {
  461. objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 0);
  462. objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"),
  463. 1);
  464. } else {
  465. objc_msgSend(openPanel, sel_registerName("setCanChooseFiles:"), 1);
  466. objc_msgSend(openPanel, sel_registerName("setCanChooseDirectories:"),
  467. 0);
  468. }
  469. objc_msgSend(openPanel, sel_registerName("setResolvesAliases:"), 0);
  470. objc_msgSend(openPanel, sel_registerName("setAllowsMultipleSelection:"),
  471. 0);
  472. panel = openPanel;
  473. } else {
  474. panel = objc_msgSend((id)objc_getClass("NSSavePanel"),
  475. sel_registerName("savePanel"));
  476. }
  477. objc_msgSend(panel, sel_registerName("setCanCreateDirectories:"), 1);
  478. objc_msgSend(panel, sel_registerName("setShowsHiddenFiles:"), 1);
  479. objc_msgSend(panel, sel_registerName("setExtensionHidden:"), 0);
  480. objc_msgSend(panel, sel_registerName("setCanSelectHiddenExtension:"), 0);
  481. objc_msgSend(panel, sel_registerName("setTreatsFilePackagesAsDirectories:"),
  482. 1);
  483. objc_msgSend(
  484. panel, sel_registerName("beginSheetModalForWindow:completionHandler:"),
  485. w->priv.window, ^(id result) {
  486. objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  487. sel_registerName("sharedApplication")),
  488. sel_registerName("stopModalWithCode:"), result);
  489. });
  490. if (objc_msgSend(objc_msgSend((id)objc_getClass("NSApplication"),
  491. sel_registerName("sharedApplication")),
  492. sel_registerName("runModalForWindow:"),
  493. panel) == (id)NSModalResponseOK) {
  494. id url = objc_msgSend(panel, sel_registerName("URL"));
  495. id path = objc_msgSend(url, sel_registerName("path"));
  496. const char *filename =
  497. (const char *)objc_msgSend(path, sel_registerName("UTF8String"));
  498. strlcpy(result, filename, resultsz);
  499. }
  500. } else if (dlgtype == WEBVIEW_DIALOG_TYPE_ALERT) {
  501. id a = objc_msgSend((id)objc_getClass("NSAlert"), sel_registerName("new"));
  502. switch (flags & WEBVIEW_DIALOG_FLAG_ALERT_MASK) {
  503. case WEBVIEW_DIALOG_FLAG_INFO:
  504. objc_msgSend(a, sel_registerName("setAlertStyle:"),
  505. NSAlertStyleInformational);
  506. break;
  507. case WEBVIEW_DIALOG_FLAG_WARNING:
  508. printf("Warning\n");
  509. objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleWarning);
  510. break;
  511. case WEBVIEW_DIALOG_FLAG_ERROR:
  512. printf("Error\n");
  513. objc_msgSend(a, sel_registerName("setAlertStyle:"), NSAlertStyleCritical);
  514. break;
  515. }
  516. objc_msgSend(a, sel_registerName("setShowsHelp:"), 0);
  517. objc_msgSend(a, sel_registerName("setShowsSuppressionButton:"), 0);
  518. objc_msgSend(a, sel_registerName("setMessageText:"), get_nsstring(title));
  519. objc_msgSend(a, sel_registerName("setInformativeText:"), get_nsstring(arg));
  520. objc_msgSend(a, sel_registerName("addButtonWithTitle:"),
  521. get_nsstring("OK"));
  522. objc_msgSend(a, sel_registerName("runModal"));
  523. objc_msgSend(a, sel_registerName("release"));
  524. }
  525. }
  526. static void webview_dispatch_cb(void *arg) {
  527. struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)arg;
  528. (context->fn)(context->w, context->arg);
  529. free(context);
  530. }
  531. WEBVIEW_API void webview_dispatch(struct webview *w, webview_dispatch_fn fn,
  532. void *arg) {
  533. struct webview_dispatch_arg *context = (struct webview_dispatch_arg *)malloc(
  534. sizeof(struct webview_dispatch_arg));
  535. context->w = w;
  536. context->arg = arg;
  537. context->fn = fn;
  538. dispatch_async_f(dispatch_get_main_queue(), context, webview_dispatch_cb);
  539. }
  540. WEBVIEW_API void webview_terminate(struct webview *w) {
  541. w->priv.should_exit = 1;
  542. }
  543. WEBVIEW_API void webview_exit(struct webview *w) {
  544. id app = objc_msgSend((id)objc_getClass("NSApplication"),
  545. sel_registerName("sharedApplication"));
  546. objc_msgSend(app, sel_registerName("terminate:"), app);
  547. }
  548. WEBVIEW_API void webview_print_log(const char *s) { printf("%s\n", s); }