WpfCefBrowser.cs 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. using System;
  2. using System.Linq;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Controls.Primitives;
  6. using System.Windows.Input;
  7. using System.Windows.Interop;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Imaging;
  10. using System.Windows.Threading;
  11. using Xilium.CefGlue.Helpers.Log;
  12. using Xilium.CefGlue.WPF.Helpers.Log;
  13. namespace Xilium.CefGlue.WPF
  14. {
  15. public class WpfCefBrowser : ContentControl, IDisposable
  16. {
  17. private static readonly Key[] HandledKeys =
  18. {
  19. Key.Tab, Key.Home, Key.End, Key.Left, Key.Right, Key.Up, Key.Down
  20. };
  21. private bool _disposed;
  22. private bool _created;
  23. private Image _browserPageImage;
  24. private WriteableBitmap _browserPageBitmap;
  25. private int _browserWidth;
  26. private int _browserHeight;
  27. private bool _browserSizeChanged;
  28. private CefBrowser _browser;
  29. private CefBrowserHost _browserHost;
  30. private WpfCefClient _cefClient;
  31. private Popup _popup;
  32. private Image _popupImage;
  33. private WriteableBitmap _popupImageBitmap;
  34. private ToolTip _tooltip;
  35. private DispatcherTimer _tooltipTimer;
  36. Dispatcher _mainUiDispatcher;
  37. private readonly ILogger _logger;
  38. public WpfCefBrowser()
  39. : this(new EmptyLogger("WpfCefBrowser"))
  40. {
  41. }
  42. public WpfCefBrowser(ILogger logger)
  43. {
  44. if (logger == null)
  45. {
  46. throw new ArgumentNullException("logger");
  47. }
  48. _logger = logger;
  49. StartUrl = "about:blank";
  50. _popup = CreatePopup();
  51. _tooltip = new ToolTip();
  52. _tooltip.StaysOpen = true;
  53. _tooltip.Visibility = Visibility.Collapsed;
  54. _tooltip.Closed += TooltipOnClosed;
  55. _tooltipTimer = new DispatcherTimer();
  56. _tooltipTimer.Interval = TimeSpan.FromSeconds(0.5);
  57. KeyboardNavigation.SetAcceptsReturn(this, true);
  58. _mainUiDispatcher = Dispatcher.CurrentDispatcher;
  59. }
  60. #region Disposable
  61. ~WpfCefBrowser()
  62. {
  63. Dispose(false);
  64. }
  65. public void Dispose()
  66. {
  67. Dispose(true);
  68. GC.SuppressFinalize(this);
  69. }
  70. protected virtual void Dispose(bool disposing)
  71. {
  72. if (disposing)
  73. {
  74. if (_tooltipTimer != null)
  75. {
  76. _tooltipTimer.Stop();
  77. }
  78. if (_browserPageImage != null)
  79. {
  80. _browserPageImage.Source = null;
  81. _browserPageImage = null;
  82. }
  83. if (_browserPageBitmap != null)
  84. {
  85. _browserPageBitmap = null;
  86. }
  87. // if (this.browserPageD3dImage != null)
  88. // this.browserPageD3dImage = null;
  89. // TODO: What's the right way of disposing the browser instance?
  90. if (_browserHost != null)
  91. {
  92. _browserHost.CloseBrowser();
  93. _browserHost = null;
  94. }
  95. if (_browser != null)
  96. {
  97. _browser.Dispose();
  98. _browser = null;
  99. }
  100. }
  101. _disposed = true;
  102. }
  103. #endregion
  104. public event LoadStartEventHandler LoadStart;
  105. public event LoadEndEventHandler LoadEnd;
  106. public event LoadingStateChangeEventHandler LoadingStateChange;
  107. public event LoadErrorEventHandler LoadError;
  108. internal void OnLoadStart(CefFrame frame)
  109. {
  110. if (this.LoadStart != null)
  111. {
  112. var e = new LoadStartEventArgs(frame);
  113. this.LoadStart(this, e);
  114. }
  115. }
  116. internal void OnLoadEnd(CefFrame frame, int httpStatusCode)
  117. {
  118. if (this.LoadEnd != null)
  119. {
  120. var e = new LoadEndEventArgs(frame, httpStatusCode);
  121. this.LoadEnd(this, e);
  122. }
  123. }
  124. internal void OnLoadingStateChange(bool isLoading, bool canGoBack, bool canGoForward)
  125. {
  126. if (this.LoadingStateChange != null)
  127. {
  128. var e = new LoadingStateChangeEventArgs(isLoading, canGoBack, canGoForward);
  129. this.LoadingStateChange(this, e);
  130. }
  131. }
  132. internal void OnLoadError(CefFrame frame, CefErrorCode errorCode, string errorText, string failedUrl)
  133. {
  134. if (this.LoadError != null)
  135. {
  136. var e = new LoadErrorEventArgs(frame, errorCode, errorText, failedUrl);
  137. this.LoadError(this, e);
  138. }
  139. }
  140. public string StartUrl { get; set; }
  141. public bool AllowsTransparency { get; set; }
  142. public override void OnApplyTemplate()
  143. {
  144. base.OnApplyTemplate();
  145. //this.browserPageD3dImage = new D3DImage();
  146. _browserPageImage = new Image()
  147. {
  148. Focusable = false,
  149. HorizontalAlignment = HorizontalAlignment.Left,
  150. VerticalAlignment = VerticalAlignment.Top,
  151. Stretch = Stretch.None
  152. };
  153. this.Content = _browserPageImage;
  154. }
  155. public void ExecuteJavaScript(string code, string url, int line)
  156. {
  157. if (_browser != null)
  158. this._browser.GetMainFrame().ExecuteJavaScript(code, url, line);
  159. }
  160. protected override Size ArrangeOverride(Size arrangeBounds)
  161. {
  162. var size = base.ArrangeOverride(arrangeBounds);
  163. if (_browserPageImage != null)
  164. {
  165. var newWidth = (int)size.Width;
  166. var newHeight = (int)size.Height;
  167. _logger.Debug("BrowserResize. Old H{0}xW{1}; New H{2}xW{3}.", _browserHeight, _browserWidth, newHeight, newWidth);
  168. if (newWidth > 0 && newHeight > 0)
  169. {
  170. if (!_created)
  171. {
  172. AttachEventHandlers(this); // TODO: ?
  173. // Create the bitmap that holds the rendered website bitmap
  174. _browserWidth = newWidth;
  175. _browserHeight = newHeight;
  176. _browserSizeChanged = true;
  177. // Find the window that's hosting us
  178. Window parentWnd = FindParentOfType<Window>(this);
  179. if (parentWnd != null)
  180. {
  181. IntPtr hParentWnd = new WindowInteropHelper(parentWnd).Handle;
  182. var windowInfo = CefWindowInfo.Create();
  183. windowInfo.SetAsWindowless(hParentWnd, AllowsTransparency);
  184. var settings = new CefBrowserSettings();
  185. _cefClient = new WpfCefClient(this);
  186. // This is the first time the window is being rendered, so create it.
  187. CefBrowserHost.CreateBrowser(windowInfo, _cefClient, settings, !string.IsNullOrEmpty(StartUrl) ? StartUrl : "about:blank");
  188. _created = true;
  189. }
  190. }
  191. else
  192. {
  193. // Only update the bitmap if the size has changed
  194. if (_browserPageBitmap == null || (_browserPageBitmap.Width != newWidth || _browserPageBitmap.Height != newHeight))
  195. {
  196. _browserWidth = newWidth;
  197. _browserHeight = newHeight;
  198. _browserSizeChanged = true;
  199. // If the window has already been created, just resize it
  200. if (_browserHost != null)
  201. {
  202. _logger.Trace("CefBrowserHost::WasResized to {0}x{1}.", newWidth, newHeight);
  203. _browserHost.WasResized();
  204. }
  205. }
  206. }
  207. }
  208. }
  209. return size;
  210. }
  211. private void AttachEventHandlers(WpfCefBrowser browser)
  212. {
  213. browser.GotKeyboardFocus += (sender, arg) =>
  214. {
  215. try
  216. {
  217. if (_browserHost != null)
  218. {
  219. _browserHost.SendFocusEvent(true);
  220. }
  221. }
  222. catch (Exception ex)
  223. {
  224. _logger.ErrorException("WpfCefBrowser: Caught exception in GotFocus()", ex);
  225. }
  226. };
  227. browser.LostKeyboardFocus += (sender, arg) =>
  228. {
  229. try
  230. {
  231. if (_browserHost != null)
  232. {
  233. _browserHost.SendFocusEvent(false);
  234. }
  235. }
  236. catch (Exception ex)
  237. {
  238. _logger.ErrorException("WpfCefBrowser: Caught exception in LostFocus()", ex);
  239. }
  240. };
  241. browser.MouseLeave += (sender, arg) =>
  242. {
  243. try
  244. {
  245. if (_browserHost != null)
  246. {
  247. CefMouseEvent mouseEvent = new CefMouseEvent()
  248. {
  249. X = 0,
  250. Y = 0
  251. };
  252. mouseEvent.Modifiers = GetMouseModifiers();
  253. _browserHost.SendMouseMoveEvent(mouseEvent, true);
  254. //_logger.Debug("Browser_MouseLeave");
  255. }
  256. }
  257. catch (Exception ex)
  258. {
  259. _logger.ErrorException("WpfCefBrowser: Caught exception in MouseLeave()", ex);
  260. }
  261. };
  262. browser.MouseMove += (sender, arg) =>
  263. {
  264. try
  265. {
  266. if (_browserHost != null)
  267. {
  268. Point cursorPos = arg.GetPosition(this);
  269. CefMouseEvent mouseEvent = new CefMouseEvent()
  270. {
  271. X = (int)cursorPos.X,
  272. Y = (int)cursorPos.Y
  273. };
  274. mouseEvent.Modifiers = GetMouseModifiers();
  275. _browserHost.SendMouseMoveEvent(mouseEvent, false);
  276. //_logger.Debug(string.Format("Browser_MouseMove: ({0},{1})", cursorPos.X, cursorPos.Y));
  277. }
  278. }
  279. catch (Exception ex)
  280. {
  281. _logger.ErrorException("WpfCefBrowser: Caught exception in MouseMove()", ex);
  282. }
  283. };
  284. browser.MouseDown += (sender, arg) =>
  285. {
  286. try
  287. {
  288. if (_browserHost != null)
  289. {
  290. Focus();
  291. Point cursorPos = arg.GetPosition(this);
  292. CefMouseEvent mouseEvent = new CefMouseEvent()
  293. {
  294. X = (int)cursorPos.X,
  295. Y = (int)cursorPos.Y,
  296. };
  297. mouseEvent.Modifiers = GetMouseModifiers();
  298. if (arg.ChangedButton == MouseButton.Left)
  299. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Left, false, arg.ClickCount);
  300. else if (arg.ChangedButton == MouseButton.Middle)
  301. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Middle, false, arg.ClickCount);
  302. else if (arg.ChangedButton == MouseButton.Right)
  303. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Right, false, arg.ClickCount);
  304. //_logger.Debug(string.Format("Browser_MouseDown: ({0},{1})", cursorPos.X, cursorPos.Y));
  305. }
  306. }
  307. catch (Exception ex)
  308. {
  309. _logger.ErrorException("WpfCefBrowser: Caught exception in MouseDown()", ex);
  310. }
  311. };
  312. browser.MouseUp += (sender, arg) =>
  313. {
  314. try
  315. {
  316. if (_browserHost != null)
  317. {
  318. Point cursorPos = arg.GetPosition(this);
  319. CefMouseEvent mouseEvent = new CefMouseEvent()
  320. {
  321. X = (int)cursorPos.X,
  322. Y = (int)cursorPos.Y,
  323. };
  324. mouseEvent.Modifiers = GetMouseModifiers();
  325. if (arg.ChangedButton == MouseButton.Left)
  326. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Left, true, arg.ClickCount);
  327. else if (arg.ChangedButton == MouseButton.Middle)
  328. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Middle, true, arg.ClickCount);
  329. else if (arg.ChangedButton == MouseButton.Right)
  330. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Right, true, arg.ClickCount);
  331. //_logger.Debug(string.Format("Browser_MouseUp: ({0},{1})", cursorPos.X, cursorPos.Y));
  332. }
  333. }
  334. catch (Exception ex)
  335. {
  336. _logger.ErrorException("WpfCefBrowser: Caught exception in MouseUp()", ex);
  337. }
  338. };
  339. browser.MouseWheel += (sender, arg) =>
  340. {
  341. try
  342. {
  343. if (_browserHost != null)
  344. {
  345. Point cursorPos = arg.GetPosition(this);
  346. CefMouseEvent mouseEvent = new CefMouseEvent()
  347. {
  348. X = (int)cursorPos.X,
  349. Y = (int)cursorPos.Y,
  350. };
  351. _browserHost.SendMouseWheelEvent(mouseEvent, 0, arg.Delta);
  352. }
  353. }
  354. catch (Exception ex)
  355. {
  356. _logger.ErrorException("WpfCefBrowser: Caught exception in MouseWheel()", ex);
  357. }
  358. };
  359. // TODO: require more intelligent processing
  360. browser.PreviewTextInput += (sender, arg) =>
  361. {
  362. if (_browserHost != null)
  363. {
  364. _logger.Debug("TextInput: text {0}", arg.Text);
  365. foreach (var c in arg.Text)
  366. {
  367. CefKeyEvent keyEvent = new CefKeyEvent()
  368. {
  369. EventType = CefKeyEventType.Char,
  370. WindowsKeyCode = (int)c,
  371. // Character = c,
  372. };
  373. keyEvent.Modifiers = GetKeyboardModifiers();
  374. _browserHost.SendKeyEvent(keyEvent);
  375. }
  376. }
  377. arg.Handled = true;
  378. };
  379. // TODO: require more intelligent processing
  380. browser.PreviewKeyDown += (sender, arg) =>
  381. {
  382. try
  383. {
  384. if (_browserHost != null)
  385. {
  386. //_logger.Debug(string.Format("KeyDown: system key {0}, key {1}", arg.SystemKey, arg.Key));
  387. CefKeyEvent keyEvent = new CefKeyEvent()
  388. {
  389. EventType = CefKeyEventType.RawKeyDown,
  390. WindowsKeyCode = KeyInterop.VirtualKeyFromKey(arg.Key == Key.System ? arg.SystemKey : arg.Key),
  391. NativeKeyCode = 0,
  392. IsSystemKey = arg.Key == Key.System,
  393. };
  394. keyEvent.Modifiers = GetKeyboardModifiers();
  395. _browserHost.SendKeyEvent(keyEvent);
  396. }
  397. }
  398. catch (Exception ex)
  399. {
  400. _logger.ErrorException("WpfCefBrowser: Caught exception in PreviewKeyDown()", ex);
  401. }
  402. arg.Handled = HandledKeys.Contains(arg.Key);
  403. };
  404. // TODO: require more intelligent processing
  405. browser.PreviewKeyUp += (sender, arg) =>
  406. {
  407. try
  408. {
  409. if (_browserHost != null)
  410. {
  411. //_logger.Debug(string.Format("KeyUp: system key {0}, key {1}", arg.SystemKey, arg.Key));
  412. CefKeyEvent keyEvent = new CefKeyEvent()
  413. {
  414. EventType = CefKeyEventType.KeyUp,
  415. WindowsKeyCode = KeyInterop.VirtualKeyFromKey(arg.Key == Key.System ? arg.SystemKey : arg.Key),
  416. NativeKeyCode = 0,
  417. IsSystemKey = arg.Key == Key.System,
  418. };
  419. keyEvent.Modifiers = GetKeyboardModifiers();
  420. _browserHost.SendKeyEvent(keyEvent);
  421. }
  422. }
  423. catch (Exception ex)
  424. {
  425. _logger.ErrorException("WpfCefBrowser: Caught exception in PreviewKeyUp()", ex);
  426. }
  427. arg.Handled = true;
  428. };
  429. browser._popup.MouseMove += (sender, arg) =>
  430. {
  431. try
  432. {
  433. if (_browserHost != null)
  434. {
  435. Point cursorPos = arg.GetPosition(this);
  436. CefMouseEvent mouseEvent = new CefMouseEvent()
  437. {
  438. X = (int)cursorPos.X,
  439. Y = (int)cursorPos.Y
  440. };
  441. mouseEvent.Modifiers = GetMouseModifiers();
  442. _browserHost.SendMouseMoveEvent(mouseEvent, false);
  443. //_logger.Debug(string.Format("Popup_MouseMove: ({0},{1})", cursorPos.X, cursorPos.Y));
  444. }
  445. }
  446. catch (Exception ex)
  447. {
  448. _logger.ErrorException("WpfCefBrowser: Caught exception in Popup.MouseMove()", ex);
  449. }
  450. };
  451. browser._popup.MouseDown += (sender, arg) =>
  452. {
  453. try
  454. {
  455. if (_browserHost != null)
  456. {
  457. Point cursorPos = arg.GetPosition(this);
  458. CefMouseEvent mouseEvent = new CefMouseEvent()
  459. {
  460. X = (int)cursorPos.X,
  461. Y = (int)cursorPos.Y
  462. };
  463. mouseEvent.Modifiers = GetMouseModifiers();
  464. _browserHost.SendMouseClickEvent(mouseEvent, CefMouseButtonType.Left, true, 1);
  465. //_logger.Debug(string.Format("Popup_MouseDown: ({0},{1})", cursorPos.X, cursorPos.Y));
  466. }
  467. }
  468. catch (Exception ex)
  469. {
  470. _logger.ErrorException("WpfCefBrowser: Caught exception in Popup.MouseDown()", ex);
  471. }
  472. };
  473. browser._popup.MouseWheel += (sender, arg) =>
  474. {
  475. try
  476. {
  477. if (_browserHost != null)
  478. {
  479. Point cursorPos = arg.GetPosition(this);
  480. int delta = arg.Delta;
  481. CefMouseEvent mouseEvent = new CefMouseEvent()
  482. {
  483. X = (int)cursorPos.X,
  484. Y = (int)cursorPos.Y
  485. };
  486. mouseEvent.Modifiers = GetMouseModifiers();
  487. _browserHost.SendMouseWheelEvent(mouseEvent, 0, delta);
  488. //_logger.Debug(string.Format("MouseWheel: ({0},{1})", cursorPos.X, cursorPos.Y));
  489. }
  490. }
  491. catch (Exception ex)
  492. {
  493. _logger.ErrorException("WpfCefBrowser: Caught exception in Popup.MouseWheel()", ex);
  494. }
  495. };
  496. }
  497. #region Handlers
  498. public void HandleAfterCreated(CefBrowser browser)
  499. {
  500. int width = 0, height = 0;
  501. bool hasAlreadyBeenInitialized = false;
  502. _mainUiDispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
  503. {
  504. if (_browser != null)
  505. {
  506. hasAlreadyBeenInitialized = true;
  507. }
  508. else
  509. {
  510. _browser = browser;
  511. _browserHost = _browser.GetHost();
  512. // _browserHost.SetFocus(IsFocused);
  513. width = (int)_browserWidth;
  514. height = (int)_browserHeight;
  515. }
  516. }));
  517. // Make sure we don't initialize ourselves more than once. That seems to break things.
  518. if (hasAlreadyBeenInitialized)
  519. return;
  520. if (width > 0 && height > 0)
  521. _browserHost.WasResized();
  522. // mainUiDispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
  523. // {
  524. // if (!string.IsNullOrEmpty(this.initialUrl))
  525. // {
  526. // NavigateTo(this.initialUrl);
  527. // this.initialUrl = string.Empty;
  528. // }
  529. // }));
  530. }
  531. internal bool GetViewRect(ref CefRectangle rect)
  532. {
  533. bool rectProvided = false;
  534. CefRectangle browserRect = new CefRectangle();
  535. // TODO: simplify this
  536. //_mainUiDispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
  537. //{
  538. try
  539. {
  540. // The simulated screen and view rectangle are the same. This is necessary
  541. // for popup menus to be located and sized inside the view.
  542. browserRect.X = browserRect.Y = 0;
  543. browserRect.Width = (int)_browserWidth;
  544. browserRect.Height = (int)_browserHeight;
  545. rectProvided = true;
  546. }
  547. catch (Exception ex)
  548. {
  549. _logger.ErrorException("WpfCefBrowser: Caught exception in GetViewRect()", ex);
  550. rectProvided = false;
  551. }
  552. //}));
  553. if (rectProvided)
  554. {
  555. rect = browserRect;
  556. }
  557. _logger.Debug("GetViewRect result provided:{0} Rect: X{1} Y{2} H{3} W{4}", rectProvided, browserRect.X, browserRect.Y, browserRect.Height, browserRect.Width);
  558. return rectProvided;
  559. }
  560. internal void GetScreenPoint(int viewX, int viewY, ref int screenX, ref int screenY)
  561. {
  562. Point ptScreen = new Point();
  563. _mainUiDispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
  564. {
  565. try
  566. {
  567. Point ptView = new Point(viewX, viewY);
  568. ptScreen = PointToScreen(ptView);
  569. }
  570. catch (Exception ex)
  571. {
  572. _logger.ErrorException("WpfCefBrowser: Caught exception in GetScreenPoint()", ex);
  573. }
  574. }));
  575. screenX = (int)ptScreen.X;
  576. screenY = (int)ptScreen.Y;
  577. }
  578. internal void HandleViewPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects, IntPtr buffer, int width, int height)
  579. {
  580. // When browser size changed - we just skip frame updating.
  581. // This is dirty precheck to do not do Invoke whenever is possible.
  582. if (_browserSizeChanged && (width != _browserWidth || height != _browserHeight)) return;
  583. _mainUiDispatcher.Invoke(DispatcherPriority.Render, new Action(delegate
  584. {
  585. // Actual browser size changed check.
  586. if (_browserSizeChanged && (width != _browserWidth || height != _browserHeight)) return;
  587. try
  588. {
  589. if (_browserSizeChanged)
  590. {
  591. _browserPageBitmap = new WriteableBitmap((int)_browserWidth, (int)_browserHeight, 96, 96, AllowsTransparency ? PixelFormats.Bgra32 : PixelFormats.Bgr32, null);
  592. _browserPageImage.Source = _browserPageBitmap;
  593. _browserSizeChanged = false;
  594. }
  595. if (_browserPageBitmap != null)
  596. {
  597. DoRenderBrowser(_browserPageBitmap, width, height, dirtyRects, buffer);
  598. }
  599. }
  600. catch (Exception ex)
  601. {
  602. _logger.ErrorException("WpfCefBrowser: Caught exception in HandleViewPaint()", ex);
  603. }
  604. }));
  605. }
  606. internal void HandlePopupPaint(int width, int height, CefRectangle[] dirtyRects, IntPtr sourceBuffer)
  607. {
  608. if (width == 0 || height == 0)
  609. {
  610. return;
  611. }
  612. _mainUiDispatcher.Invoke(
  613. DispatcherPriority.Render,
  614. new Action(
  615. () =>
  616. {
  617. int stride = width * 4;
  618. int sourceBufferSize = stride * height;
  619. _logger.Debug("RenderPopup() Bitmap H{0}xW{1}, Browser H{2}xW{3}", _popupImageBitmap.Height, _popupImageBitmap.Width, width, height);
  620. foreach (CefRectangle dirtyRect in dirtyRects)
  621. {
  622. _logger.Debug(
  623. string.Format(
  624. "Dirty rect [{0},{1},{2},{3}]",
  625. dirtyRect.X,
  626. dirtyRect.Y,
  627. dirtyRect.Width,
  628. dirtyRect.Height));
  629. if (dirtyRect.Width == 0 || dirtyRect.Height == 0)
  630. {
  631. continue;
  632. }
  633. int adjustedWidth = dirtyRect.Width;
  634. int adjustedHeight = dirtyRect.Height;
  635. Int32Rect sourceRect = new Int32Rect(dirtyRect.X, dirtyRect.Y, adjustedWidth, adjustedHeight);
  636. _popupImageBitmap.WritePixels(sourceRect, sourceBuffer, sourceBufferSize, stride, dirtyRect.X, dirtyRect.Y);
  637. }
  638. }));
  639. }
  640. private void DoRenderBrowser(WriteableBitmap bitmap, int browserWidth, int browserHeight, CefRectangle[] dirtyRects, IntPtr sourceBuffer)
  641. {
  642. int stride = browserWidth * 4;
  643. int sourceBufferSize = stride * browserHeight;
  644. _logger.Debug("DoRenderBrowser() Bitmap H{0}xW{1}, Browser H{2}xW{3}", bitmap.Height, bitmap.Width, browserHeight, browserWidth);
  645. if (browserWidth == 0 || browserHeight == 0)
  646. {
  647. return;
  648. }
  649. foreach (CefRectangle dirtyRect in dirtyRects)
  650. {
  651. _logger.Debug(string.Format("Dirty rect [{0},{1},{2},{3}]", dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height));
  652. if (dirtyRect.Width == 0 || dirtyRect.Height == 0)
  653. {
  654. continue;
  655. }
  656. // If the window has been resized, make sure we never try to render too much
  657. int adjustedWidth = (int)dirtyRect.Width;
  658. //if (dirtyRect.X + dirtyRect.Width > (int) bitmap.Width)
  659. //{
  660. // adjustedWidth = (int) bitmap.Width - (int) dirtyRect.X;
  661. //}
  662. int adjustedHeight = (int)dirtyRect.Height;
  663. //if (dirtyRect.Y + dirtyRect.Height > (int) bitmap.Height)
  664. //{
  665. // adjustedHeight = (int) bitmap.Height - (int) dirtyRect.Y;
  666. //}
  667. // Update the dirty region
  668. Int32Rect sourceRect = new Int32Rect((int)dirtyRect.X, (int)dirtyRect.Y, adjustedWidth, adjustedHeight);
  669. bitmap.WritePixels(sourceRect, sourceBuffer, sourceBufferSize, stride, (int)dirtyRect.X, (int)dirtyRect.Y);
  670. // int adjustedWidth = browserWidth;
  671. // if (browserWidth > (int)bitmap.Width)
  672. // adjustedWidth = (int)bitmap.Width;
  673. //
  674. // int adjustedHeight = browserHeight;
  675. // if (browserHeight > (int)bitmap.Height)
  676. // adjustedHeight = (int)bitmap.Height;
  677. //
  678. // int sourceBufferSize = browserWidth * browserHeight * 4;
  679. // int stride = browserWidth * 4;
  680. //
  681. // Int32Rect sourceRect = new Int32Rect(0, 0, adjustedWidth, adjustedHeight);
  682. // bitmap.WritePixels(sourceRect, sourceBuffer, sourceBufferSize, stride, 0, 0);
  683. }
  684. }
  685. internal void OnPopupShow(bool show)
  686. {
  687. if (_popup == null)
  688. {
  689. return;
  690. }
  691. _mainUiDispatcher.Invoke(new Action(() => _popup.IsOpen = show));
  692. }
  693. internal void OnPopupSize(CefRectangle rect)
  694. {
  695. _mainUiDispatcher.Invoke(
  696. new Action(
  697. () =>
  698. {
  699. _popupImageBitmap = null;
  700. _popupImageBitmap = new WriteableBitmap(
  701. rect.Width,
  702. rect.Height,
  703. 96,
  704. 96,
  705. PixelFormats.Bgr32,
  706. null);
  707. _popupImage.Source = this._popupImageBitmap;
  708. _popup.Width = rect.Width;
  709. _popup.Height = rect.Height;
  710. _popup.HorizontalOffset = rect.X;
  711. _popup.VerticalOffset = rect.Y;
  712. }));
  713. }
  714. internal bool OnTooltip(string text)
  715. {
  716. if (string.IsNullOrEmpty(text))
  717. {
  718. _tooltipTimer.Stop();
  719. UpdateTooltip(null);
  720. }
  721. else
  722. {
  723. _tooltipTimer.Tick += (sender, args) => UpdateTooltip(text);
  724. _tooltipTimer.Start();
  725. }
  726. return true;
  727. }
  728. #endregion
  729. #region Utils
  730. /// <summary>
  731. /// Finds a parent of the specific type
  732. /// </summary>
  733. private static T FindParentOfType<T>(DependencyObject obj) where T : DependencyObject
  734. {
  735. DependencyObject parentObj = VisualTreeHelper.GetParent(obj);
  736. if (parentObj == null)
  737. return null;
  738. // Try to type cast the parent to the desired type.
  739. // If the cast succeeds, we've found the desired parent.
  740. T parent = parentObj as T;
  741. if (parent != null)
  742. return parent;
  743. // If we get here, the current parent wasn't of the right type, so keep looking recursively
  744. return FindParentOfType<T>(parentObj);
  745. }
  746. private static CefEventFlags GetMouseModifiers()
  747. {
  748. CefEventFlags modifiers = new CefEventFlags();
  749. if (Mouse.LeftButton == MouseButtonState.Pressed)
  750. modifiers |= CefEventFlags.LeftMouseButton;
  751. if (Mouse.MiddleButton == MouseButtonState.Pressed)
  752. modifiers |= CefEventFlags.MiddleMouseButton;
  753. if (Mouse.RightButton == MouseButtonState.Pressed)
  754. modifiers |= CefEventFlags.RightMouseButton;
  755. return modifiers;
  756. }
  757. private static CefEventFlags GetKeyboardModifiers()
  758. {
  759. CefEventFlags modifiers = new CefEventFlags();
  760. if (Keyboard.Modifiers == ModifierKeys.Alt)
  761. modifiers |= CefEventFlags.AltDown;
  762. if (Keyboard.Modifiers == ModifierKeys.Control)
  763. modifiers |= CefEventFlags.ControlDown;
  764. if (Keyboard.Modifiers == ModifierKeys.Shift)
  765. modifiers |= CefEventFlags.ShiftDown;
  766. return modifiers;
  767. }
  768. private Popup CreatePopup()
  769. {
  770. var popup = new Popup
  771. {
  772. Child = this._popupImage = CreatePopupImage(),
  773. PlacementTarget = this,
  774. Placement = PlacementMode.Relative
  775. };
  776. return popup;
  777. }
  778. private Image CreatePopupImage()
  779. {
  780. var temp = new Image();
  781. RenderOptions.SetBitmapScalingMode(temp, BitmapScalingMode.NearestNeighbor);
  782. temp.Stretch = Stretch.None;
  783. temp.HorizontalAlignment = HorizontalAlignment.Left;
  784. temp.VerticalAlignment = VerticalAlignment.Top;
  785. temp.Source = _popupImageBitmap;
  786. return temp;
  787. }
  788. private void UpdateTooltip(string text)
  789. {
  790. _mainUiDispatcher.Invoke(
  791. DispatcherPriority.Render,
  792. new Action(
  793. () =>
  794. {
  795. if (string.IsNullOrEmpty(text))
  796. {
  797. _tooltip.IsOpen = false;
  798. }
  799. else
  800. {
  801. _tooltip.Placement = PlacementMode.Mouse;
  802. _tooltip.Content = text;
  803. _tooltip.IsOpen = true;
  804. _tooltip.Visibility = Visibility.Visible;
  805. }
  806. }));
  807. _tooltipTimer.Stop();
  808. }
  809. private void TooltipOnClosed(object sender, RoutedEventArgs routedEventArgs)
  810. {
  811. _tooltip.Visibility = Visibility.Collapsed;
  812. _tooltip.Placement = PlacementMode.Absolute;
  813. }
  814. #endregion
  815. #region Methods
  816. public void NavigateTo(string url)
  817. {
  818. // Remove leading whitespace from the URL
  819. url = url.TrimStart();
  820. if (_browser != null)
  821. _browser.GetMainFrame().LoadUrl(url);
  822. else
  823. StartUrl = url;
  824. }
  825. public void LoadString(string content, string url)
  826. {
  827. // Remove leading whitespace from the URL
  828. url = url.TrimStart();
  829. if (_browser != null)
  830. _browser.GetMainFrame().LoadString(content, url);
  831. }
  832. public bool CanGoBack()
  833. {
  834. if (_browser != null)
  835. return _browser.CanGoBack;
  836. else
  837. return false;
  838. }
  839. public void GoBack()
  840. {
  841. if (_browser != null)
  842. _browser.GoBack();
  843. }
  844. public bool CanGoForward()
  845. {
  846. if (_browser != null)
  847. return _browser.CanGoForward;
  848. else
  849. return false;
  850. }
  851. public void GoForward()
  852. {
  853. if (_browser != null)
  854. _browser.GoForward();
  855. }
  856. public void Refresh()
  857. {
  858. if (_browser != null)
  859. _browser.Reload();
  860. }
  861. #endregion
  862. }
  863. }