2017-02-06 28 views
3

我正在使用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

我不認爲Geckofx的webidl compiler是目前已足夠先進以生成回調構造函數。

選項1. - 增強MutationObserver源代碼。

您可以手動修改MutationObserver源代碼以添加必要的構造函數回調。然後重新編譯Geckofx。 (我沒有看到這是多麼困難)

選項2 - 使用舊式的突變事件。

public class DOMSubtreeModifiedEventListener : nsIDOMEventListener 
{ 
    ... // Implement HandleEvent 
} 

然後像(也許在DocumentCompleted事件處理程序):

_domSubtreeModifiedEventListener = new DOMSubtreeModifiedEventListener(this); 

    var target = Xpcom.QueryInterface<nsIDOMEventTarget>(body); 
    using (nsAString subtreeModified = new nsAString("DOMSubtreeModified")) 
     target.AddEventListener(subtreeModified, _domSubtreeModifiedEventListener, true, false, 0); 

選項3 - 利用閒置+檢查。

添加一個winforms Application.idle事件處理程序 - 並檢查文檔,以瞭解它何時準備就緒。

選項4. - 注入一個javascript回調。

(正如您已經提到的) - 此示例正在等待調整完成後。

基本上注:"<body onresize=fireResizedEventAfterDelay()>":再注入這樣的事情:

string fireResizedEventAfterDelayScript = "<script>\n" + 
      "var resizeListner;" + 
      "var msDelay = 20;" + 
      "function fireResizedEventAfterDelay() {" + 
      "clearTimeout(resizeListner);" + 
      "resizeListner = setTimeout(function() { document.dispatchEvent (new MessageEvent('resized')); }, msDelay);" + 
      "}\n" + 
      "</script>\n"; 

然後在C#:

browser.AddMessageEventListener("resized", (s) => runafterImDone()) 
+0

你好,我試圖選項2,然而,只有適當的參考,我能找到對於'var target = Xpcom.QueryInterface (X);''在'Window.cs'中是'WindowRoot'。然而,似乎'GeckoWindow.cs'擴展了'Window.cs',它並沒有公開前述的'WindowRoot'屬性。我是否正在處理這種不正確的方式?我在原始問題中包含了我嘗試的相關部分。 –

+1

var body = browser.Document?.Body?.DOMHtmlElement; – Tom

+0

啊,我現在感到很傻......謝謝湯姆! –