CommonOffscreenBrowserAdapter.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using System;
  2. using System.Threading.Tasks;
  3. using Xilium.CefGlue.Common.Helpers;
  4. using Xilium.CefGlue.Common.Helpers.Logger;
  5. using Xilium.CefGlue.Common.InternalHandlers;
  6. using Xilium.CefGlue.Common.Platform;
  7. using Xilium.CefGlue.Common.Shared.Helpers;
  8. namespace Xilium.CefGlue.Common
  9. {
  10. internal class CommonOffscreenBrowserAdapter : CommonBrowserAdapter, IOffscreenCefBrowserHost
  11. {
  12. private static readonly TimeSpan ResizeDelay = TimeSpan.FromMilliseconds(50);
  13. private bool _isVisible = true;
  14. private Func<CefRectangle> _getViewRectOverride;
  15. public CommonOffscreenBrowserAdapter(object eventsEmitter, string name, IOffScreenControlHost control, IOffScreenPopupHost popup, ILogger logger)
  16. : base(eventsEmitter, name, control, logger) {
  17. Popup = popup;
  18. }
  19. protected new IOffScreenControlHost Control => (IOffScreenControlHost) base.Control;
  20. private IOffScreenPopupHost Popup { get; }
  21. protected override void InnerDispose()
  22. {
  23. Control.RenderSurface.Dispose();
  24. Popup.RenderSurface.Dispose();
  25. }
  26. private int Width => Control.RenderSurface.Width;
  27. private int Height => Control.RenderSurface.Height;
  28. private void SendMouseClickEvent(CefMouseEvent mouseEvent, CefMouseButtonType mouseButton, bool isMouseUp, int clickCount)
  29. {
  30. BrowserHost?.SendMouseClickEvent(mouseEvent, mouseButton, isMouseUp, clickCount);
  31. }
  32. protected override void HandleGotFocus()
  33. {
  34. WithErrorHandling(nameof(HandleGotFocus), () =>
  35. {
  36. BrowserHost?.SendFocusEvent(true);
  37. });
  38. }
  39. private void HandleLostFocus()
  40. {
  41. WithErrorHandling(nameof(HandleLostFocus), () =>
  42. {
  43. BrowserHost?.SendFocusEvent(false);
  44. });
  45. }
  46. private void HandleMouseMove(CefMouseEvent mouseEvent)
  47. {
  48. WithErrorHandling(nameof(HandleMouseMove), () =>
  49. {
  50. BrowserHost?.SendMouseMoveEvent(mouseEvent, false);
  51. });
  52. }
  53. private void HandleMouseLeave(CefMouseEvent mouseEvent)
  54. {
  55. WithErrorHandling(nameof(HandleMouseLeave), () =>
  56. {
  57. BrowserHost?.SendMouseMoveEvent(mouseEvent, true);
  58. });
  59. }
  60. private void HandleMouseButtonDown(IOffScreenControlHost control, CefMouseEvent mouseEvent, CefMouseButtonType mouseButton, int clickCount)
  61. {
  62. WithErrorHandling(nameof(HandleMouseButtonDown), () =>
  63. {
  64. control.Focus();
  65. if (BrowserHost != null)
  66. {
  67. SendMouseClickEvent(mouseEvent, mouseButton, false, clickCount);
  68. }
  69. });
  70. }
  71. private void HandleMouseButtonUp(CefMouseEvent mouseEvent, CefMouseButtonType mouseButton)
  72. {
  73. WithErrorHandling(nameof(HandleMouseButtonUp), () =>
  74. {
  75. if (BrowserHost != null)
  76. {
  77. SendMouseClickEvent(mouseEvent, mouseButton, true, 1);
  78. }
  79. });
  80. }
  81. private void HandleMouseWheel(CefMouseEvent mouseEvent, int deltaX, int deltaY)
  82. {
  83. WithErrorHandling(nameof(HandleMouseWheel), () =>
  84. {
  85. BrowserHost?.SendMouseWheelEvent(mouseEvent, deltaX, deltaY);
  86. });
  87. }
  88. private void HandleTextInput(string text, out bool handled)
  89. {
  90. var _handled = false;
  91. WithErrorHandling(nameof(HandleMouseWheel), () =>
  92. {
  93. if (BrowserHost != null)
  94. {
  95. foreach (var c in text)
  96. {
  97. var keyEvent = new CefKeyEvent()
  98. {
  99. EventType = CefKeyEventType.Char,
  100. WindowsKeyCode = c,
  101. Character = c
  102. };
  103. BrowserHost?.SendKeyEvent(keyEvent);
  104. }
  105. _handled = true;
  106. }
  107. });
  108. handled = _handled;
  109. }
  110. private void HandleKeyPress(CefKeyEvent keyEvent, out bool handled)
  111. {
  112. WithErrorHandling(nameof(HandleKeyPress), () =>
  113. {
  114. if (BrowserHost != null)
  115. {
  116. //_logger.Debug(string.Format("KeyDown: system key {0}, key {1}", arg.SystemKey, arg.Key));
  117. BrowserHost?.SendKeyEvent(keyEvent);
  118. }
  119. });
  120. handled = false;
  121. }
  122. private void HandleDragEnter(CefMouseEvent mouseEvent, CefDragData dragData, CefDragOperationsMask effects)
  123. {
  124. WithErrorHandling(nameof(HandleDragEnter), () =>
  125. {
  126. BrowserHost?.DragTargetDragEnter(dragData, mouseEvent, effects);
  127. BrowserHost?.DragTargetDragOver(mouseEvent, effects);
  128. });
  129. }
  130. private void HandleDragOver(CefMouseEvent mouseEvent, CefDragOperationsMask effects)
  131. {
  132. WithErrorHandling(nameof(HandleDragOver), () =>
  133. {
  134. BrowserHost?.DragTargetDragOver(mouseEvent, effects);
  135. });
  136. // TODO
  137. //e.Effects = currentDragDropEffects;
  138. //e.Handled = true;
  139. }
  140. private void HandleDragLeave()
  141. {
  142. WithErrorHandling(nameof(HandleDragLeave), () =>
  143. {
  144. BrowserHost?.DragTargetDragLeave();
  145. });
  146. }
  147. private void HandleDrop(CefMouseEvent mouseEvent, CefDragOperationsMask effects)
  148. {
  149. WithErrorHandling(nameof(HandleDrop), () =>
  150. {
  151. BrowserHost?.DragTargetDragOver(mouseEvent, effects);
  152. BrowserHost?.DragTargetDrop(mouseEvent);
  153. });
  154. }
  155. private void HandleVisibilityChanged(bool isVisible)
  156. {
  157. if (isVisible == _isVisible)
  158. {
  159. // visiblity didn't change at all
  160. return;
  161. }
  162. WithErrorHandling(nameof(HandleVisibilityChanged), () =>
  163. {
  164. if (BrowserHost != null)
  165. {
  166. _isVisible = isVisible;
  167. if (isVisible)
  168. {
  169. BrowserHost.WasHidden(false);
  170. // workaround cef OSR bug (https://bitbucket.org/chromiumembedded/cef/issues/2483/osr-invalidate-does-not-generate-frame)
  171. // we notify browser of a resize and return height+1px on next GetViewRect call
  172. // then restore the original size back again
  173. ActionTask.Run(async () =>
  174. {
  175. _getViewRectOverride = () =>
  176. {
  177. return new CefRectangle(0, 0, Width, Height + 1);
  178. };
  179. BrowserHost.WasResized();
  180. await Task.Delay(ResizeDelay);
  181. if (BrowserHost != null)
  182. {
  183. _getViewRectOverride = null;
  184. BrowserHost.WasResized();
  185. }
  186. });
  187. }
  188. else
  189. {
  190. BrowserHost.WasHidden(true);
  191. }
  192. }
  193. });
  194. }
  195. private void HandleScreenInfoChanged(float deviceScaleFactor)
  196. {
  197. WithErrorHandling(nameof(HandleScreenInfoChanged), () =>
  198. {
  199. Control.RenderSurface.DeviceScaleFactor = deviceScaleFactor;
  200. Popup.RenderSurface.DeviceScaleFactor = deviceScaleFactor;
  201. BrowserHost?.WasResized();
  202. // Might cause a crash due to a SurfaceSync check in chromium code.
  203. //
  204. // Fixed in chromium versions >= 79.0.3909.0 (https://chromium-review.googlesource.com/c/chromium/src/+/1792459)
  205. //
  206. //_browserHost?.NotifyScreenInfoChanged();
  207. });
  208. }
  209. protected override void HandleControlSizeChanged(CefSize size)
  210. {
  211. if (IsBrowserCreated)
  212. {
  213. ResizeBrowser(size.Width, size.Height);
  214. }
  215. else
  216. {
  217. CreateBrowser(size.Width, size.Height);
  218. }
  219. }
  220. private void AttachEventHandlers(IOffScreenControlHost control)
  221. {
  222. control.LostFocus += HandleLostFocus;
  223. control.MouseMoved += HandleMouseMove;
  224. control.MouseLeave += HandleMouseLeave;
  225. control.MouseButtonPressed += HandleMouseButtonDown;
  226. control.MouseButtonReleased += HandleMouseButtonUp;
  227. control.MouseWheelChanged += HandleMouseWheel;
  228. control.KeyDown += HandleKeyPress;
  229. control.KeyUp += HandleKeyPress;
  230. control.TextInput += HandleTextInput;
  231. control.DragEnter += HandleDragEnter;
  232. control.DragOver += HandleDragOver;
  233. control.DragLeave += HandleDragLeave;
  234. control.Drop += HandleDrop;
  235. }
  236. protected override CommonCefClient CreateCefClient()
  237. {
  238. return new CommonCefClient(this, new CommonCefRenderHandler(this, _logger), _logger);
  239. }
  240. protected override void SetupBrowserView(CefWindowInfo windowInfo, int width, int height, IntPtr hostViewHandle)
  241. {
  242. AttachEventHandlers(Control);
  243. AttachEventHandlers(Popup);
  244. Control.ScreenInfoChanged += HandleScreenInfoChanged;
  245. Control.VisibilityChanged += HandleVisibilityChanged;
  246. Control.RenderSurface.Resize(width, height);
  247. // Find the window that's hosting us
  248. windowInfo.SetAsWindowless(hostViewHandle, Control.RenderSurface.AllowsTransparency);
  249. }
  250. protected override void OnBrowserHostCreated(CefBrowserHost browserHost)
  251. {
  252. if (Width > 0 && Height > 0)
  253. {
  254. browserHost.WasResized();
  255. }
  256. }
  257. protected void ResizeBrowser(int newWidth, int newHeight)
  258. {
  259. if (Width == newWidth && Height == newHeight)
  260. {
  261. return;
  262. }
  263. Control.RenderSurface.Resize(newWidth, newHeight);
  264. BrowserHost?.WasResized();
  265. _logger.Debug($"Browser resized {newWidth}x{newHeight}");
  266. }
  267. protected override bool OnBrowserClose(CefBrowser browser)
  268. {
  269. Cleanup(browser);
  270. // According to cef documentation:
  271. // If no OS window exists (window rendering disabled) returning false will cause the browser object to be destroyed immediately
  272. return false;
  273. }
  274. #region IOffscreenCefBrowserHost
  275. void IOffscreenCefBrowserHost.GetViewRect(out CefRectangle rect)
  276. {
  277. rect = GetViewRect();
  278. }
  279. private CefRectangle GetViewRect()
  280. {
  281. var result = _getViewRectOverride?.Invoke() ?? new CefRectangle(0, 0, Width, Height);
  282. // The simulated screen and view rectangle are the same. This is necessary
  283. // for popup menus to be located and sized inside the view.
  284. if (result.Width <= 0 || result.Height <= 0)
  285. {
  286. // NOTE: width and height must be > 0, otherwise cef will blow up
  287. return new CefRectangle(0, 0, Math.Max(1, result.Width), Math.Max(1, result.Height));
  288. }
  289. return result;
  290. }
  291. void IOffscreenCefBrowserHost.GetScreenPoint(int viewX, int viewY, ref int screenX, ref int screenY)
  292. {
  293. GetScreenPoint(viewX, viewY, ref screenX, ref screenY);
  294. }
  295. private void GetScreenPoint(int viewX, int viewY, ref int screenX, ref int screenY)
  296. {
  297. // TODO
  298. //var point = new Point(0, 0);
  299. //WithErrorHandling(nameof(GetScreenPoint), () =>
  300. //{
  301. // point = _control.PointToScreen(new Point(viewX, viewY), _controlRenderHandler.DeviceScaleFactor);
  302. //});
  303. //screenX = point.X;
  304. //screenY = point.Y;
  305. }
  306. void IOffscreenCefBrowserHost.GetScreenInfo(CefScreenInfo screenInfo)
  307. {
  308. screenInfo.DeviceScaleFactor = Control.RenderSurface.DeviceScaleFactor;
  309. }
  310. void IOffscreenCefBrowserHost.HandlePopupShow(bool show)
  311. {
  312. WithErrorHandling(nameof(IOffscreenCefBrowserHost.HandlePopupShow), () =>
  313. {
  314. if (show)
  315. {
  316. Popup.Open();
  317. }
  318. else
  319. {
  320. Popup.Close();
  321. }
  322. });
  323. }
  324. void IOffscreenCefBrowserHost.HandlePopupSizeChange(CefRectangle rect)
  325. {
  326. WithErrorHandling(nameof(IOffscreenCefBrowserHost.HandlePopupSizeChange), () =>
  327. {
  328. Popup.RenderSurface.Resize(rect.Width, rect.Height);
  329. Popup.MoveAndResize(rect.X, rect.Y, rect.Width, rect.Height);
  330. });
  331. }
  332. void IOffscreenCefBrowserHost.HandleCursorChange(IntPtr cursorHandle)
  333. {
  334. WithErrorHandling((nameof(IOffscreenCefBrowserHost.HandleCursorChange)), () =>
  335. {
  336. Control.SetCursor(cursorHandle);
  337. });
  338. }
  339. void IOffscreenCefBrowserHost.HandleViewPaint(IntPtr buffer, int width, int height, CefRectangle[] dirtyRects, bool isPopup)
  340. {
  341. if (_getViewRectOverride != null)
  342. {
  343. return;
  344. }
  345. OffScreenRenderSurface renderHandler;
  346. if (isPopup)
  347. {
  348. renderHandler = Popup.RenderSurface;
  349. }
  350. else
  351. {
  352. renderHandler = Control.RenderSurface;
  353. }
  354. const string ScopeName = nameof(IOffscreenCefBrowserHost.HandleViewPaint);
  355. WithErrorHandling(ScopeName, () =>
  356. {
  357. renderHandler?.Render(buffer, width, height, dirtyRects)
  358. .ContinueWith(t => HandleException(ScopeName, t.Exception), TaskContinuationOptions.OnlyOnFaulted);
  359. });
  360. }
  361. void IOffscreenCefBrowserHost.HandleStartDragging(CefBrowser browser, CefDragData dragData, CefDragOperationsMask allowedOps, int x, int y)
  362. {
  363. WithErrorHandling(nameof(IOffscreenCefBrowserHost.HandleStartDragging), async () =>
  364. {
  365. var result = await Control.StartDrag(dragData, allowedOps, x, y);
  366. BrowserHost.DragSourceEndedAt(x, y, result);
  367. BrowserHost.DragSourceSystemDragEnded();
  368. });
  369. }
  370. void IOffscreenCefBrowserHost.HandleUpdateDragCursor(CefBrowser browser, CefDragOperationsMask operation)
  371. {
  372. Control.UpdateDragCursor(operation);
  373. }
  374. #endregion
  375. }
  376. }