Я использую GeckoFx
, чтобы выполнить вход на определенный сайт. Этот веб-сайт редактирует страницу с новой информацией, если логин завершится неудачно (или требуется дополнительная аутентификация, например ReCaptcha). К сожалению, очень важно иметь доступ к событию при обновлении страницы. Я попробовал многочисленные подходы в основномИспользование MutationObserver в GeckoFx с C#?
- Непрестанная проверка, если
uri
все та же при каждом попытки входа и последующей проверки по конкретному элементу в вопросе (чтобы увидеть, еслиdisplay: none
свойство было изменено. (Это привело к бесконечный цикл, как кажетсяGeckoFx
обновляет страницу неблокирующим способом, заставляя программу перейти в бесконечный цикл). - Спящий в течение ~ 5 секунд между запросами на вход и использованием вышеупомянутой отметки
uri
. Все это (предположительно, я хватался за соломинку) замораживал браузер в течение 5 секунд и до сих пор не обновил страницу - Поиск кодовой базы
GeckoFx
для определенного события, когда страница обновляется, аналогично событиюDocumentCompleted
(нет такой удачи).
Наиболее распространенный подход, который я прочитал (и тот, который имеет наибольший смысл), заключается в использовании MutationObserver
. Похоже, что все ответы через Интернет включают инъекцию Javascript
для выполнения требуемой задачи. Видя, как весь мой фон программирования не коснулся веб-разработки, я стараюсь придерживаться того, что знаю.
Вот мой подход, к сожалению, этого мало.
public class GeckoTestWebLogin
{
private readonly string _user;
private readonly string _pass;
public GeckoWebBrowser Gweb;
public Uri LoginUri { get; } = new Uri("https://website.com/login/");
public bool LoginCompleted { get; private set; } = false;
public bool Loaded { get; private set; } = false;
public GeckoTestWebLogin(string user, string pass)
{
_user = user;
_pass = pass;
Xpcom.EnableProfileMonitoring = false;
Xpcom.Initialize("Firefox");
//this code is for testing purposes, it will be removed upon project completion
CookieManager.RemoveAll();
Gweb = new GeckoWebBrowser();
Gweb.DocumentCompleted += DocLoaded;
//right about here is where I get lost, where can I set a callback method for the observer to report back to? Is this even how it works?
MutationObserver mutationObserver = new MutationObserver(Gweb.Window.DomWindow, (nsISupports)Gweb.Document.DomObject);
}
private void TestObservedEvent(string parms, object[] objs)
{
MessageBox.Show("The page was changed @ " + DateTime.Now);
}
public void DocLoaded(object obj, GeckoDocumentCompletedEventArgs e)
{
Loaded = true;
if (Gweb.Url != LoginUri) return;
AttemptLogin();
}
private void AttemptLogin()
{
GeckoElementCollection elements = Gweb.Document.GetElementsByTagName("input");
foreach (GeckoHtmlElement element in elements)
{
switch (element.Id)
{
case "username":
element.SetAttribute("value", _user);
break;
case "password":
element.SetAttribute("value", _pass);
break;
case "importantchangedinfo":
GeckoHtmlElement authcodeModal =
(GeckoHtmlElement)
Gweb.Document.GetElementsByClassName("login_modal").First();
if (authcodeModal.Attributes["style"].NodeValue != "display: none")
{
InputForm form = new InputForm { InputDescription = "Captcha Required!" };
form.ShowDialog();
elements.FirstOrDefault(x => x.Id == "captchabox")?.SetAttribute("value", form.Input);
}
break;
}
}
elements.FirstOrDefault(x => x.Id == "Login")?.Click();
}
public void Login()
{
//this will cause the DocLoaded event to fire after completion
Gweb.Navigate(LoginUri.ToString());
}
}
Как указано в приведенном выше коде в комментариях, я полностью потерял в
MutationObserver mutationObserver = new MutationObserver(Gweb.Window.DomWindow, (nsISupports)Gweb.Document.DomObject);
Я не могу найти что-нибудь в источнике GeckoFx
«s для MutationObserver
, что позволило бы мне установить обратный вызов/событие/whathaveyou. Является ли мой подход правильным способом заниматься вещами или у меня нет других вариантов, кроме как ввести Javascript
на страницу?
Большое спасибо, заблаговременно.
Вот моя попытка вариант 2 в ответ Тома:
(Добавлено в GeckoTestWebLogin)
public void DocLoaded(object obj, GeckoDocumentCompletedEventArgs e)
{
Loaded = true;
if (Gweb.Url != LoginUri) return;
MutationEventListener mutationListener = new MutationEventListener();
mutationListener.OnDomMutation += TestObservedEvent;
nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>(/*Lost here*/);
using (nsAString modified = new nsAString("DOMSubtreeModified"))
target.AddEventListener(modified, mutationListener, true, false, 0);
AttemptLogin();
}
MutationEventListener.cs:
public delegate void OnDomMutation(/*DomMutationArgs args*/);
public class MutationEventListener : nsIDOMEventListener
{
public event OnDomMutation OnDomMutation;
public void HandleEvent(nsIDOMEvent domEvent)
{
OnDomMutation?.Invoke(/*new DomMutationArgs(domEvent, this)*/);
}
}
Здравствуйте, я пытал вариант 2, однако, единственным подходящим справочным я мог бы найти для 'var target = Xpcom.QueryInterface (X);' был 'WindowRoot' в' Window.cs'. Однако, казалось бы, в то время как 'GeckoWindow.cs' расширяет' Window.cs', он не раскрывает вышеупомянутое свойство WindowRoot. Неужели я ошибаюсь? Я включил соответствующие части моей попытки в свой первоначальный вопрос. –
var body = browser.Document? .Body? .DOMHtmlElement; – Tom
Ах, теперь я чувствую себя глупо ... спасибо Тому! –