Эмуляция событий мыши в GeckoFx [C#]

0

Итак, разобрался я, как эмулировать события мыши в 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/

0
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Авторизация
*
*
Регистрация
*
*
*
Пароль не введен
*
Генерация пароля
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x