UIPlatformWebViewApple.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. //
  2. // Copyright (c) 2014-2017, THUNDERBEAST GAMES LLC All rights reserved
  3. // Copyright (c) 2011-2017, The Game Engine Company LLC (Apache License 2.0)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #import <Foundation/Foundation.h>
  24. /*
  25. Notes:
  26. 1) This is based on the Cocoa/iOS webview from the LoomSDK: https://github.com/LoomSDK/LoomSDK/blob/master/loom/common/platform/platformWebViewCocoa.mm
  27. 2) macOS mouse tracking complicates the platform's webview and causes issues with the Atomic UI controls beneath it
  28. */
  29. #ifdef ATOMIC_PLATFORM_OSX
  30. #import <WebKit/WebKit.h>
  31. typedef WebView CocoaWebView;
  32. typedef NSRect CocoaRect;
  33. typedef NSView CocoaView;
  34. #define CocoaMakeRect NSMakeRect
  35. #else
  36. #import <UIKit/UIKit.h>
  37. typedef UIWebView CocoaWebView;
  38. typedef CGRect CocoaRect;
  39. typedef UIView CocoaView;
  40. #define CocoaMakeRect CGRectMake
  41. #endif
  42. #include "../../IO/Log.h"
  43. #include "UIPlatformWebView.h"
  44. using namespace Atomic;
  45. //_________________________________________________________________________
  46. // Helpers
  47. //_________________________________________________________________________
  48. #ifdef ATOMIC_PLATFORM_IOS
  49. static float pixelsToPoints(float pixels)
  50. {
  51. float scale = [UIScreen mainScreen].scale;
  52. return pixels / scale;
  53. }
  54. #endif
  55. static CocoaView* GetMainView()
  56. {
  57. #ifdef ATOMIC_PLATFORM_OSX
  58. return [[[[NSApplication sharedApplication] windows] objectAtIndex:0] contentView];
  59. #else
  60. return [[[[UIApplication sharedApplication] keyWindow] rootViewController] view];
  61. #endif
  62. }
  63. #ifdef ATOMIC_PLATFORM_OSX
  64. static NSWindow* GetMainWindow()
  65. {
  66. return [[[NSApplication sharedApplication] windows] objectAtIndex:0];
  67. }
  68. #endif
  69. @interface AtomicWebViewDelegate : NSObject
  70. {
  71. UIPlatformWebView* uiWebView;
  72. }
  73. -(id)initWithWebView:(UIPlatformWebView*)webview;
  74. @end
  75. @interface WebViewRef : NSObject {
  76. CocoaRect _rect;
  77. CocoaWebView* _view;
  78. AtomicWebViewDelegate* _delegate;
  79. }
  80. @property CocoaRect rect;
  81. @property (retain) CocoaWebView* view;
  82. @property (retain) AtomicWebViewDelegate* delegate;
  83. @end
  84. @implementation WebViewRef
  85. @synthesize view = _view;
  86. @synthesize delegate = _delegate;
  87. - (CocoaRect)rect
  88. {
  89. return _rect;
  90. }
  91. - (void)setRect:(CocoaRect)newRect;
  92. {
  93. _rect = newRect;
  94. CocoaRect frame;
  95. #ifdef ATOMIC_PLATFORM_OSX
  96. frame = newRect;
  97. #else
  98. frame.size.width = pixelsToPoints(newRect.size.width);
  99. frame.size.height = pixelsToPoints(newRect.size.height);
  100. frame.origin.x = pixelsToPoints(newRect.origin.x);
  101. frame.origin.y = getMainView().frame.size.height - frame.size.height - pixelsToPoints(newRect.origin.y);
  102. #endif
  103. self.view.frame = frame;
  104. }
  105. @end
  106. static NSMutableDictionary* gWebViews;
  107. NSMutableDictionary* webViews()
  108. {
  109. if(gWebViews == NULL)
  110. gWebViews = [[NSMutableDictionary dictionary] retain];
  111. return gWebViews;
  112. }
  113. WebViewRef* GetWebViewRef(AtomicWebViewHandle handle)
  114. {
  115. return [webViews() objectForKey:[NSNumber numberWithInt:handle]];
  116. }
  117. //_________________________________________________________________________
  118. // WebView Delegate
  119. //_________________________________________________________________________
  120. @implementation AtomicWebViewDelegate
  121. -(id)initWithWebView:(UIPlatformWebView*)webview
  122. {
  123. self = [self init];
  124. uiWebView = webview;
  125. return self;
  126. }
  127. #ifdef ATOMIC_PLATFORM_OSX
  128. - (void)webView:(CocoaWebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
  129. {
  130. if(frame == [sender mainFrame])
  131. {
  132. NSURLRequest* request = frame.provisionalDataSource.request;
  133. NSString *urlString = [[request URL] absoluteString];
  134. uiWebView->OnRequestSent([urlString cStringUsingEncoding:1]);
  135. }
  136. }
  137. - (void)webView:(CocoaWebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
  138. {
  139. if(frame == [sender mainFrame])
  140. {
  141. NSInteger code = [error code];
  142. NSString *codeString = [NSString stringWithFormat:@"WebKit Error code: %ld",(long)code];
  143. uiWebView->OnError([codeString cStringUsingEncoding:1]);
  144. }
  145. }
  146. - (void)webView:(CocoaWebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
  147. {
  148. if(frame == [sender mainFrame])
  149. {
  150. NSInteger code = [error code];
  151. NSString *codeString = [NSString stringWithFormat:@"WebKit Error code: %ld",(long)code];
  152. uiWebView->OnError([codeString cStringUsingEncoding:1]);
  153. }
  154. }
  155. #else
  156. - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
  157. {
  158. NSString *urlString = [[request URL] absoluteString];
  159. callback(payload, WEBVIEW_REQUEST_SENT, [urlString cStringUsingEncoding:1]);
  160. return YES;
  161. }
  162. - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
  163. {
  164. NSInteger code = [error code];
  165. NSString *codeString = [NSString stringWithFormat:@"WebKit Error code: %ld",(long)code];
  166. callback(payload, WEBVIEW_REQUEST_ERROR, [codeString cStringUsingEncoding:1]);
  167. }
  168. #endif
  169. @end
  170. namespace Atomic
  171. {
  172. bool UIPlatformWebView::PlatformCreateWebView()
  173. {
  174. if (webViewHandle_ != UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  175. {
  176. ATOMIC_LOGWARNING("UIPlatformWebView::CreatePlatformWebView() - Attempting to create platform webview with valid handle");
  177. return false;
  178. }
  179. webViewHandle_ = webViewHandleCounter_++;
  180. webViewLookup_[webViewHandle_] = this;
  181. IntVector2 rootPos = ConvertToRoot();
  182. CocoaRect bounds;
  183. bounds.size.width = (float) GetWidth();
  184. bounds.size.height = (float) GetHeight();
  185. float mainHeight = GetMainView().frame.size.height;
  186. bounds.origin.x = (float) rootPos.x_;
  187. bounds.origin.y = mainHeight - bounds.size.height - rootPos.y_;
  188. CocoaWebView* webView = [[[CocoaWebView alloc] initWithFrame:bounds] retain];
  189. AtomicWebViewDelegate* delegate = [[[AtomicWebViewDelegate alloc] initWithWebView:this] retain];
  190. #ifdef ATOMIC_PLATFORM_OSX
  191. [webView setFrameLoadDelegate:(id)delegate];
  192. [webView setWantsLayer:YES];
  193. #else
  194. [webView setDelegate:delegate];
  195. #endif
  196. WebViewRef *ref = [WebViewRef alloc];
  197. ref.view = webView;
  198. ref.rect = webView.frame;
  199. ref.delegate = delegate;
  200. [webViews() setObject:ref forKey:[NSNumber numberWithInt:webViewHandle_]];
  201. if (!queuedRequest_.Empty())
  202. {
  203. LoadURL(queuedRequest_);
  204. queuedRequest_.Clear();
  205. }
  206. return true;
  207. }
  208. void UIPlatformWebView::PlatformShowWebView(bool visible)
  209. {
  210. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  211. {
  212. ATOMIC_LOGWARNING("UIPlatformWebView::ShowPlatformWebView() - Invalid webview handle");
  213. return;
  214. }
  215. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  216. if (!webView)
  217. {
  218. ATOMIC_LOGERROR("UIPlatformWebView::ShowPlatformWebView() - Unable to retrieve webview from handle");
  219. return;
  220. }
  221. if (visible)
  222. {
  223. #ifdef ATOMIC_PLATFORM_OSX
  224. if (webView.superview != GetMainView())
  225. {
  226. [GetMainView() addSubview:webView];
  227. }
  228. #endif
  229. }
  230. else
  231. {
  232. #ifdef ATOMIC_PLATFORM_OSX
  233. if (webView.superview == GetMainView())
  234. {
  235. [webView removeFromSuperview];
  236. }
  237. #endif
  238. }
  239. }
  240. void UIPlatformWebView::PlatformDestroyWebView()
  241. {
  242. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  243. {
  244. return;
  245. }
  246. webViewLookup_.Erase(webViewHandle_);
  247. WebViewRef* ref = GetWebViewRef(webViewHandle_);
  248. CocoaWebView* webView = ref.view;
  249. AtomicWebViewDelegate* delegate = ref.delegate;
  250. [webView removeFromSuperview];
  251. [webView release];
  252. [delegate release];
  253. [ref release];
  254. [webViews() removeObjectForKey:[NSNumber numberWithInt:webViewHandle_]];
  255. webViewHandle_ = UI_PLATFORM_WEBVIEW_INVALID_HANDLE;
  256. }
  257. void UIPlatformWebView::PlatformPositionWebView()
  258. {
  259. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  260. {
  261. return;
  262. }
  263. WebViewRef* ref = GetWebViewRef(webViewHandle_);
  264. IntVector2 rootPos = ConvertToRoot(IntVector2( 0, 0));
  265. CocoaRect frame;
  266. frame.size.width = (float) GetWidth();
  267. frame.size.height = (float) GetHeight();
  268. float mainHeight = GetMainView().frame.size.height;
  269. frame.origin.x = (float) rootPos.x_;
  270. frame.origin.y = mainHeight - frame.size.height - rootPos.y_;
  271. if (frame.origin.x == ref.rect.origin.x &&
  272. frame.origin.y == ref.rect.origin.y &&
  273. frame.size.width == ref.rect.size.width &&
  274. frame.size.height == ref.rect.size.height )
  275. {
  276. return;
  277. }
  278. ref.rect = frame;
  279. }
  280. void UIPlatformWebView::DestroyAll()
  281. {
  282. NSArray* keys = [webViews() allKeys];
  283. for (int i=0; i<[keys count]; i++)
  284. {
  285. NSNumber* num = [keys objectAtIndex:i];
  286. const WeakPtr<UIPlatformWebView>& ptr = webViewLookup_[[num intValue]];
  287. if (ptr.NotNull())
  288. {
  289. ptr->PlatformDestroyWebView();
  290. }
  291. }
  292. }
  293. bool UIPlatformWebView::GoBack()
  294. {
  295. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  296. {
  297. return false;
  298. }
  299. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  300. if (![webView canGoBack]) return false;
  301. [webView goBack];
  302. return true;
  303. }
  304. bool UIPlatformWebView::GoForward()
  305. {
  306. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  307. {
  308. return false;
  309. }
  310. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  311. if (![webView canGoForward]) return false;
  312. [webView goForward];
  313. return true;
  314. }
  315. bool UIPlatformWebView::CanGoBack() const
  316. {
  317. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  318. {
  319. return false;
  320. }
  321. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  322. return [webView canGoBack];
  323. }
  324. bool UIPlatformWebView::CanGoForward() const
  325. {
  326. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  327. {
  328. return false;
  329. }
  330. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  331. return [webView canGoForward];
  332. }
  333. void UIPlatformWebView::PauseAll()
  334. {
  335. #ifdef ATOMIC_PLATFORM_IOS
  336. NSArray* keys = [webViews() allKeys];
  337. for (int i=0; i<[keys count]; i++)
  338. {
  339. NSNumber* num = [keys objectAtIndex:i];
  340. WebViewRef* ref = getWebViewRef([num intValue]);
  341. CocoaWebView* webView = ref.view;
  342. [webView setDelegate:nil];
  343. }
  344. #endif
  345. }
  346. void UIPlatformWebView::ResumeAll()
  347. {
  348. #ifdef ATOMIC_PLATFORM_IOS
  349. NSArray* keys = [webViews() allKeys];
  350. for (int i=0; i<[keys count]; i++)
  351. {
  352. NSNumber* num = [keys objectAtIndex:i];
  353. WebViewRef* ref = getWebViewRef([num intValue]);
  354. CocoaWebView* webView = ref.view;
  355. [webView setDelegate:(id)ref.delegate];
  356. }
  357. #endif
  358. }
  359. void UIPlatformWebView::Reload()
  360. {
  361. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  362. {
  363. return;
  364. }
  365. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  366. #ifdef ATOMIC_PLATFORM_OSX
  367. [[webView mainFrame] reload];
  368. #else
  369. [webView reload];
  370. #endif
  371. }
  372. void UIPlatformWebView::LoadURL(const String& url)
  373. {
  374. if (webViewHandle_ == UI_PLATFORM_WEBVIEW_INVALID_HANDLE)
  375. {
  376. queuedRequest_ = url;
  377. return;
  378. }
  379. CocoaWebView* webView = GetWebViewRef(webViewHandle_).view;
  380. NSURL* urlObj = [NSURL URLWithString:[NSString stringWithUTF8String:url.CString()]];
  381. NSURLRequest* request = [NSURLRequest requestWithURL:urlObj];
  382. #ifdef ATOMIC_PLATFORM_OSX
  383. [[webView mainFrame] loadRequest:request];
  384. #else
  385. [webView loadRequest:request];
  386. #endif
  387. }
  388. }