Эмуляция событий мыши в GeckoFx [C#]
Итак, разобрался я, как эмулировать события мыши в GeckoFx.
Все достаточно просто.
Исходники в конце статьи
В JavaScript есть такой класс, как MouseEvent и метод initMouseEvent(). Вот с помощью них и еще парочки классов можно реализовать эмуляцию событий мыши в GeckoFx.
В движке уже реализованы данные классы и их просто нужно использовать в правильном порядке
Движок поддерживает следующие типы событий мыши:
- mousedown;
- mouseup;
- mousemove;
- mouseover;
- mouseout;
- contextmenu;
- dblclick;
- click.
С помощью следующего метода-расширения можно эмулировать события мыши:
internal static class GeckoExtensionMethods
{
/// <summary>
/// Эмулирует событие мыши
/// </summary>
/// <param name="webBrowser"></param>
/// <param name="element">Элемент, которому будет отправляться событие</param>
/// <param name="aTypeEvent">Тип события</param>
/// <param name="screenX"></param>
/// <param name="screenY"></param>
/// <param name="clientX">Координата мыши X</param>
/// <param name="clientY">Координата мыши Y</param>
public static void MouseEventEmulation( this GeckoWebBrowser webBrowser, GeckoElement element, string aTypeEvent,
int screenX, int screenY, int clientX, int clientY )
{
nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>( element.DomObject );
DomEventArgs evt = webBrowser.DomDocument.CreateEvent( "MouseEvent" );
DomMouseEventArgs mouseEvent = (DomMouseEventArgs)DomEventArgs.Create( evt.DomEvent );
mouseEvent.InitMouseEvent( aTypeEvent, true, true, webBrowser.Window, 0, screenX, screenY, clientX, clientY,
false, false, false, false, 0, Gecko.DOM.DomEventTarget.Create( target ) );
target.DispatchEvent( mouseEvent.DomEvent );
}
}
Пример использования: движение мыши с точки 0,0 к какому-то элементу:
/// <summary>
/// Эмулирует движение мыши к указанному элементу (с точки 1,1)
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private async Task MoveMouseToElement( GeckoElement element )
{
var startPoint = new Point( 1, 1 );
// определяем координаты конечной точки
var endPoint = new Point( element.GetBoundingClientRect().Left, element.GetBoundingClientRect().Top );
// примитивное движение мышью к нужной точке
do
{
// эмулируем событие mousemove
this._webBrowser.MouseEventEmulation( this._webBrowser.Document.Body, "mousemove", 0, 0, startPoint.X,
startPoint.Y );
if ( startPoint.X < endPoint.X )
{
startPoint.X++;
}
if ( startPoint.Y < endPoint.Y )
{
startPoint.Y++;
}
// задержка
await TaskEx.Delay( 10 );
}
while ( startPoint.X < endPoint.X || startPoint.Y < endPoint.Y );
}
Для того, чтобы эмулировать движение мыши по всему документу, нужно передавать в метод MouseEventEmulation в качестве первого аргумента Document.Body (т.е. ссылку на весь документ), если же нужно сделать клик по элементу — передавать элемент, по которому нужно произвести клик
Рассмотрим простой пример, где будем заходить на страницу Google Translate, вводить в поле какой-то текст и нажимать на кнопку «Прослушать».
Создаем новый проект GeckoMouseEvents (.NET Framework v4), подключаем либы GeckoFx 33, устанавливаем Microsoft Async и скачиваем Xulrunner 33.
Атрибуты главной формы: Size = 627; 480, StartPosition = CenterScreen, Text = GeckoMouseEvents, WindowState = Maximized.
Бросаем на форму Panel (Location = 0;0, Size = 610; 25, Name = pnl1), кнопку на панель (Location = 0;0, Size = 75; 23, Name = btnStart, Text = Начнем).
Теперь создаем статический класс с методом, который будет осуществлять эмуляцию событий мыши. Назовем его GeckoExtensionMethods:
internal static class GeckoExtensionMethods
{
/// <summary>
/// Эмулирует указанное событие мыши
/// </summary>
/// <param name="webBrowser"></param>
/// <param name="element">Элемент, которому будет отправляться событие</param>
/// <param name="aTypeEvent">Тип события</param>
/// <param name="screenX"></param>
/// <param name="screenY"></param>
/// <param name="clientX">Координата мыши X</param>
/// <param name="clientY">Координата мыши Y</param>
public static void MouseEventEmulation( this GeckoWebBrowser webBrowser, GeckoElement element, string aTypeEvent,
int screenX, int screenY, int clientX, int clientY )
{
nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>( element.DomObject );
DomEventArgs evt = webBrowser.DomDocument.CreateEvent( "MouseEvent" );
DomMouseEventArgs mouseEvent = (DomMouseEventArgs)DomEventArgs.Create( evt.DomEvent );
mouseEvent.InitMouseEvent( aTypeEvent, true, true, webBrowser.Window, 0, screenX, screenY, clientX, clientY,
false, false, false, false, 0, Gecko.DOM.DomEventTarget.Create( target ) );
target.DispatchEvent( mouseEvent.DomEvent );
}
}
Теперь создадим класс, в котором будет логика работы (загрузка страницы, ввод текста и т.д.). Назовем его EmulMouseEvents:
internal class EmulMouseEvents
{
private readonly GeckoWebBrowser _webBrowser; // ссылка на браузер
private readonly Timer _loadingTimer; // таймер загрузки
private bool _loading; // указывает на статус загрузки
public EmulMouseEvents( GeckoWebBrowser webBrowser )
{
this._webBrowser = webBrowser;
this._loadingTimer = new Timer { Interval = 5000 };
this._loadingTimer.Tick += this._loadingTimer_Tick;
}
private void _loadingTimer_Tick( object sender, EventArgs e )
{
// останавливаем таймер
this._loadingTimer.Stop();
// считаем, что загрузка завершилась
this._loading = false;
}
/// <summary>
/// Ожидает загрузки страницы
/// </summary>
private async Task WaitForLoading()
{
while ( this._loading )
{
await TaskEx.Delay( 200 );
}
}
/// <summary>
/// Загружает указанную страницу и ждет завершения загрузки
/// </summary>
/// <param name="url">Url страницы</param>
private async Task Navigate( string url )
{
this._loading = true;
this._webBrowser.Navigate( url );
this._loadingTimer.Start();
await this.WaitForLoading();
}
/// <summary>
/// Печатает текст
/// </summary>
/// <param name="element">Куда печатать</param>
/// <param name="text">Текст</param>
/// <returns></returns>
private async Task WriteText( GeckoElement element, string text )
{
Random random = new Random();
var htmlEl = element as GeckoHtmlElement;
if ( htmlEl == null )
{
return;
}
for ( int i = 0; i < text.Length; i++ )
{
htmlEl.InnerHtml = text.Substring( 0, i + 1 );
await TaskEx.Delay( random.Next( 100, 350 ) );
}
}
/// <summary>
/// Эмулирует движение мыши к указанному элементу (с точки 1,1)
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private async Task MoveMouseToElement( GeckoElement element )
{
var startPoint = new Point( 1, 1 );
// определяем координаты конечной точки
var endPoint = new Point( element.GetBoundingClientRect().Left, element.GetBoundingClientRect().Top );
// примитивное движение мышью к нужной точке
do
{
// эмулируем событие mousemove
this._webBrowser.MouseEventEmulation( this._webBrowser.Document.Body, "mousemove", 0, 0, startPoint.X,
startPoint.Y );
if ( startPoint.X < endPoint.X )
{
startPoint.X++;
}
if ( startPoint.Y < endPoint.Y )
{
startPoint.Y++;
}
// задержка
await TaskEx.Delay( 10 );
}
while ( startPoint.X < endPoint.X || startPoint.Y < endPoint.Y );
}
/// <summary>
/// Эмулирует нажатие левой кнопкной мыши на элемент
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
private async Task MouseClickOnElement( GeckoElement element )
{
// эмулируем нажатие на элемент
// вместо mousedown и mouseup можно использовать click, но он не везде работает
this._webBrowser.MouseEventEmulation( element, "mousedown", 0, 0, 0, 0 );
await TaskEx.Delay( 120 );
this._webBrowser.MouseEventEmulation( element, "mouseup", 0, 0, 0, 0 );
}
/// <summary>
/// Старт
/// </summary>
public async void Start()
{
// открываем страницу переводчика и ждем загрузки
await this.Navigate( "http://translate.google.com/" );
// получаем поле для ввода текста
GeckoElement textElement = this._webBrowser.Document.GetElementById( "source" );
// "плавный" набор поискового запроса
const string query = "Hello, World!";
// вводим поисковый запрос
await this.WriteText( textElement, query );
// получаем кнопку "прослушать"
GeckoElement listenElement = this._webBrowser.Document.GetElementById( "gt-src-listen" );
// эмулируем движение мыши к кнопке "Прослушать"
await this.MoveMouseToElement( listenElement );
// эмулируем нажатие на кнопку "Прослушать"
await this.MouseClickOnElement( listenElement );
}
}
Точка входа здесь метод Start().
После начала открытия страницы происходит запуск таймера с интервалом в 5 секунд. По прошествии 5 секунд считаем, что страница загрузилась. Далее получаем поле для ввода текста с идентификатором source, выполняем набор текста, получаем кнопку «Прослушать» с идентификатором gr-src-listener, эмулируем движение к этому объекту (которое мы все равно не увидим но счетчики типа Яндекс.Метрика такое движение зафиксируют) и, после, эмулируем нажатие на кнопку.
Так-с, теперь весь этот код нужно вызвать. Переходим на главную форму, в конструкторе формы производим инициализацию GeckoFx:
public FrmMain()
{
this.InitializeComponent();
// инициализация Xulrunner
Xpcom.Initialize( Application.StartupPath + "\\xulrunner\\" );
this.InitializeComponent();
// создаем экземпляр класса GeckoWebBrowser
this._webBrowser = new GeckoWebBrowser
{
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom,
Width = this.Width,
Height = this.Height - this.pnl1.Height - 5,
Top = this.pnl1.Bottom + 5
};
// устанавливаем UserAgent браузера в Firefox 33
GeckoPreferences.User[ "general.useragent.override" ] =
"Mozilla/5.0 (Windows NT 6.1; rv:33.0) Gecko/20150101 Firefox/33.0";
// добавляем контрол браузера на форму
this.Controls.Add( this._webBrowser );
}
И добавляем обработчик события нажатия на кнопку:
private void btnStart_Click( object sender, EventArgs e )
{
// создаем экземпляр класса EmulMouseEvents
var clickClass = new EmulMouseEvents( this._webBrowser );
// начинаем выполнение
clickClass.Start();
}
Не забываем добавить поле главной формы:
private readonly GeckoWebBrowser _webBrowser;

Исходники:
Источник: https://www.softez.pp.ua/