簡體   English   中英

在GeckoFx中使用MutationObserver和C#?

[英]Using MutationObserver in GeckoFx with C#?

我正在使用GeckoFx來登錄特定網站。 如果登錄失敗(或需要其他身份驗證,例如ReCaptcha),此網站會使用新信息編輯頁面。 不幸的是,在頁面更新時訪問事件至關重要。 我主要嘗試了很多方法

  • 連續檢查每次登錄嘗試時uri是否仍然相同以及隨后檢查相關特定元素(查看display: none屬性是否已更改。)這導致無限循環,因為GeckoFx似乎更新了頁面以非阻塞方式,導致程序進入無限循環)
  • 在登錄請求和使用上述uri檢查之間休眠約5秒。 所有這一切(可以預見,我正在抓住稻草)凍結瀏覽器5秒鍾仍然無法更新頁面
  • 在頁面更新時搜索GeckoFx代碼庫以查找特定事件,類似於DocumentCompleted事件(沒有這樣的運氣)。

我讀過的最常見的方法(以及最有意義的方法)是使用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);

我似乎無法在GeckoFxMutationObserver源代碼中找到任何允許我設置回調/事件/ whathaveyou的內容。 我的方法是正確的方法來處理事情還是除了將Javascript注入頁面之外我沒有其他選擇?

非常感謝,提前謝謝你。

以下是我在Tom的答案中對選項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)*/);
    }
}

我不認為Geckofx的webidl編譯器目前已經足夠先進,可以生成回調構造函數。

選項1. - 增強MutationObserver源。

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

選項2. - 使用舊式Mutation事件。

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())

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM