我正在使用GeckoFx
來執行登錄到特定網站。如果登錄失敗(或需要額外的身份驗證,如ReCaptcha),該網站會使用新信息編輯頁面。不幸的是,當頁面更新時訪問一個事件是至關重要的。我已經試過許多方法主要在C#中使用GeckoFx中的MutationObserver?
- 連連檢查,如果
uri
還是在每次登錄嘗試,問題中的特定元素在隨後的檢查一樣(看是否display: none
性質發生了變化。(這導致因爲它似乎GeckoFx
一個無限循環更新頁面的非阻塞的方式,導致程序進入無限循環) - 睡登錄請求之間〜5秒,使用上述
uri
支票。這一切的事(預見的是,我抓住吸管)凍結瀏覽器5秒鐘,仍然無法更新頁面 - 當頁面更新類似於
DocumentCompleted
事件(沒有這樣的運氣)時,在GeckoFx
代碼庫中搜索特定事件。
我讀過的最常見的方法(最有意義的方法之一)是使用MutationObserver
。看來,互聯網上的所有答案都涉及注入Javascript
以執行必要的任務。鑑於我所有的編程背景都沒有觸及Web開發,我試圖堅持我所知道的。
這是我迄今爲止的方法,不幸的是,它並不多。
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
的來源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);''在'Window.cs'中是'WindowRoot'。然而,似乎'GeckoWindow.cs'擴展了'Window.cs',它並沒有公開前述的'WindowRoot'屬性。我是否正在處理這種不正確的方式?我在原始問題中包含了我嘗試的相關部分。 –
var body = browser.Document?.Body?.DOMHtmlElement; – Tom
啊,我現在感到很傻......謝謝湯姆! –