Модификация браузера от Mozilla в C#

Если в своих проектах на C# вы используете браузер, основанный на движке от Mozilla Gecko, то вы наверняка заметили что его стандартные возможности, желают оставлять лучшего. Имеется в виду, что если вы используете чистую, а не модифицированную библиотеку Skybound.GeckoFX. Конечно следует выразить благодарность разработчикам этого движка и этой библиотеки. Ведь лучше иметь немного, чем не иметь ничего. Но на этой библиотеке далеко не “уедешь” (хотя смотря у кого какие цели). Потому что она имеет самый минимально необходимый функционал. Но ее можно расширить до колоссального перечня различных возможностей. Если вы хотите управлять программно некоторыми операциями, то необходимо объявить необходимые интерфейсы и описать реализацию нужных вам методов. В этой статье я продемонстрирую, как расширить функциональные особенности библиотеки Skybound.GeckoFX.

Помните, что после каждой модификации библиотеки, необходимо в вашем проекте заменять ссылку, на новую пересобранную версию.

Удаление cookie

Возможность удаления cookie, является одной из самых необходимых особенностей браузера. Для того чтобы иметь возможность удалять cookie в разрабатываемом браузере, необходимо в модуль nsInterfaces.cs, добавить описание следующего интерфейса:

[Guid("AAAB6710-0F2C-11d5-A53B-0010A401EB10"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
interface nsICookieManager
{
	void RemoveAll();
	nsISimpleEnumerator Enumerator();
	void Remove(AUTF8String aDomain, nsACString aName, AUTF8String aPath,
                bool aBlocked);
}

Интерфейс имеет два метода по работе с cookie: RemoveAll и Remove. Мы реализуем метод по удалению всех cookie сразу, для этого нам необходимо описать реализацию метода RemoveAll. Описание методов по реализации дополнительных возможностей я предлагаю выносить в модуль GeckoWebBrowser.cs. Сейчас добавьте в модуль следующий код:

public void DeleteCookie()
{
	nsICookieManager CookieMan;
	CookieMan = Xpcom.GetService("@mozilla.org/cookiemanager;1");
	CookieMan = Xpcom.QueryInterface(CookieMan);
	CookieMan.RemoveAll();
}

Теперь в вашем проекте, при необходимости удаления cookie, вы должны вызывать метод компонента GeckoWebBrowser – DeleteCookie().

geckoWebBrowser.DeleteCookie();

Настройка прокси-сервера

Также не менее важной особенностью браузера является возможность работы через прокси-сервер. Настроить использование прокси-сервера просто. Но если понадобиться сделать и автоматическую авторизацию, то здесь необходимо уже применить смекалку. Сначала необходимо указать стандартные настройки (хост и порт) прокси-сервера. Для этого нужно добавить в проект следующий код:

Указываем, что мы настраиваем прокси-сервер вручную:

GeckoPreferences.User["network.proxy.type"] = 1;

Указываем используемый нами хост:

GeckoPreferences.User["network.proxy.http"] = "88.78.4.77";

Ну и соответственно номер порта:

GeckoPreferences.User["network.proxy.http_port"] = 80;

Если для прокси-сервера указание хоста и порта не является достаточным и он требует авторизацию по логину и паролю, то здесь ситуация меняется не в лучшую для нас с вами сторону. Потому что без явного указания логина и пароля, нас будет ждать разочарование в виде появляющегося такого диалогового окна с предложением введения логина и пароля.

Диалоговое окно для ввода логина и пароля от прокси-сервера

Для того чтобы указать недостающие настройки в самом проекте, а не вводить каждый раз логин и пароль вручную, придется вновь внести изменения в библиотеку. Поскольку стандартные возможности для указания логина и пароля в коде проекта, у самой этой библиотеки отсутствуют. К счастью, не все так печально как может показаться на первый взгляд. Здесь даже нет необходимости объявлять новый интерфейс. Все, что необходимо сделать, это открыть модуль PromptService.cs. Найти в нем метод PromptAuth и заменить его на ниже приведенный код:

public bool PromptAuth(nsIDOMWindow aParent, IntPtr aChannel, int level,
nsIAuthInformation authInfo, string checkboxLabel, IntPtr aCheckValue)
{
	Object login = GeckoPreferences.User["network.proxy.login"];
	Object password = GeckoPreferences.User["network.proxy.password"];

	if (login != null)                
		nsString.Set(authInfo.SetUsername, login.ToString());

	if (password != null)               
	nsString.Set(authInfo.SetPassword, password.ToString());
            
	return true; 			
}

Далее в коде проекта, в нужном вам месте необходимо добавить следующие две строки:

GeckoPreferences.User["network.proxy.login"] = "login";
GeckoPreferences.User["network.proxy.password"] = "password";

Все, теперь браузер будет корректно проходить авторизацию, если в этом конечно будет необходимость.

Эмуляция событий

Эмулировать события от мыши или клавиатуры так же является одной из самых важных необходимостей браузера. Особенно сейчас, когда многое количество различного рода защит реализованных с помощью JavaScript (от накрутки, спама и т.д.) требуют некого действия пользователя. То кликнуть мышью, то подвести курсор и так далее в этом же стиле. Вот здесь собственно и возникает необходимость в эмуляции некого рода действий пользователя. Для того чтобы получить возможность эмулировать события в нашем проекте, нам потребуется снова в модуль nsInterfaces.cs, добавить следующие интерфейсы:

[Guid("1acdb2ba-1dd2-11b2-95bc-9542495d2569"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
internal interface nsIDOMDocumentView
{
	nsIDOMAbstractView GetDefaultView();
}

[Guid("f51ebade-8b1a-11d3-aae7-0010830123b4"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMAbstractView
{
	nsIDOMDocumentView GetDocument();
}

[Guid("46b91d66-28e2-11d4-ab1e-0010830123b4"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMDocumentEvent
{
	nsIDOMEvent CreateEvent(nsAString eventType);
}
	
[Guid("028e0e6e-8b01-11d3-aae7-0010838a3123"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMKeyEvent : nsIDOMUIEvent
{
	// nsIDOMEvent:
	new void GetType(nsAString aType);
	new nsIDOMEventTarget GetTarget();
	new nsIDOMEventTarget GetCurrentTarget();
	new ushort GetEventPhase();
	new bool GetBubbles();
	new bool GetCancelable();
	new IntPtr GetTimeStamp(); // DOMTimeStamp
	new void StopPropagation();
	new void PreventDefault();
	new void InitEvent(nsACString eventTypeArg, bool canBubbleArg, 
	bool cancelableArg);
		
	// nsIDOMUIEvent:
	new IntPtr GetView(); // nsIDOMAbstractView
	new int GetDetail();
	new void InitUIEvent(nsAString typeArg, bool canBubbleArg, 
	bool cancelableArg, nsIDOMAbstractView viewArg, int detailArg); 
		
	// nsIDOMKeyEvent:
	uint GetCharCode();
	uint GetKeyCode();
	bool GetAltKey();
	bool GetCtrlKey();
	bool GetShiftKey();
	bool GetMetaKey();
	
	void InitKeyEvent(nsAString typeArg, bool canBubbleArg, 
	bool cancelableArg, nsIDOMAbstractView viewArg, bool ctrlKeyArg, 
	bool altKeyArg, bool shiftKeyArg, bool metaKeyArg, 
	uint keyCodeArg, uint charCodeArg); 
}
	
[Guid("ff751edc-8b02-aae7-0010-8301838a3123"), ComImport, InterfaceType(
ComInterfaceType.InterfaceIsIUnknown)]
interface nsIDOMMouseEvent : nsIDOMUIEvent
{
	// nsIDOMEvent:
	new void GetType(nsAString aType);
	new nsIDOMEventTarget GetTarget();
	new nsIDOMEventTarget GetCurrentTarget();
	new ushort GetEventPhase();
	new bool GetBubbles();
	new bool GetCancelable();
	new UInt64 GetTimeStamp(); // DOMTimeStamp
	new void StopPropagation();
	new void PreventDefault();
	new void InitEvent(nsACString eventTypeArg, bool canBubbleArg, 
	bool cancelableArg);
		
	// nsIDOMUIEvent:
	new IntPtr GetView(); // nsIDOMAbstractView
	new int GetDetail();
	new void InitUIEvent(nsAString typeArg, bool canBubbleArg, 
	bool cancelableArg, nsIDOMAbstractView viewArg, int detailArg); 
		
	// nsIDOMMouseEvent:
	int GetScreenX();
	int GetScreenY();
	int GetClientX();
	int GetClientY();
	bool GetCtrlKey();
	bool GetShiftKey();
	bool GetAltKey();
	int GetMetaKey();
	ushort GetButton();
	nsIDOMEventTarget GetRelatedTarget();
		
	void InitMouseEvent(nsAString typeArg, bool canBubbleArg, 
	bool cancelableArg, nsIDOMAbstractView viewArg, int detailArg, 
	int screenXArg, int screenYArg, int clientXArg, int clientYArg, 
	bool ctrlKeyArg, bool altKeyArg, bool shiftKeyArg, bool metaKeyArg, 
	ushort buttonArg, nsIDOMEventTarget relatedTargetArg);
}

После того как мы добавили недостающие нам интерфейсы, необходимо в уже знакомый нам модуль GeckoWebBrowser.cs добавить два метода для реализации эмуляции события от мыши и клавиатуры:

public void EmulationMouseEvent(GeckoElement Element, string ATypeEvent, 
			int screenX, int screenY, int clientX, int clientY)
{
    nsIDOMEventTarget Target = Xpcom.QueryInterface<nsIDOMEventTarget>
	(Element.DomObject);
    nsIDOMEvent evt = ((nsIDOMDocumentEvent)this.Document.DomObject
	).CreateEvent(new nsAString("MouseEvents"));
    nsIDOMMouseEvent MouseEvent = (nsIDOMMouseEvent)evt;
    nsIDOMAbstractView Window = Xpcom.QueryInterface<nsIDOMAbstractView>
	(this.Window.DomWindow);

    nsAString TypeEvent = new nsAString(ATypeEvent);

    MouseEvent.InitMouseEvent(TypeEvent, true, true, Window, 0, screenX, 
		screenY, clientX, clientY, false, false, false, false, 0, null);

    Target.DispatchEvent(MouseEvent);

    TypeEvent.Dispose();
}

public void EmulationKeyboardEvent(GeckoElement Element, 
			string ATypeEvent, uint KeyCode)
{
    nsIDOMEventTarget Target = 	Xpcom.QueryInterface<nsIDOMEventTarget>
	(Element.DomObject);
    nsIDOMEvent evt = ((nsIDOMDocumentEvent)this.Document.DomObject
	).CreateEvent(new nsAString("KeyboardEvent"));
    nsIDOMKeyEvent Keyboard = (nsIDOMKeyEvent)evt;
    nsIDOMAbstractView Window = Xpcom.QueryInterface<nsIDOMAbstractView>
	(this.Window.DomWindow);

    nsAString TypeEvent = new nsAString(ATypeEvent);

    Keyboard.InitKeyEvent(TypeEvent, true, true, Window, false, false,
	false, false, KeyCode, 0);

    Target.DispatchEvent(Keyboard);

    TypeEvent.Dispose();
}

Сейчас у нас все готово и необходимо перекомпилировать библиотеку, а затем ее будет можно подключать к проекту. В качестве примера эмуляции клика рассмотрим следующий код:

geckoWebBrowser.EmulationMouseEvent(element, "click", 0, 0, 0, 0);

В результате выполнения этого кода сгенерируется событие “onclick”, для некого элемента в координатах left = 0, top = 0, width = 0 и height = 0.