【问题标题】:Using MutationObserver in GeckoFx with C#?使用 C# 在 GeckoFx 中使用 MutationObserver?
【发布时间】:2017-06-22 21:53:13
【问题描述】:

我正在使用GeckoFx 登录特定网站。如果登录失败(或需要额外的身份验证,例如 ReCaptcha),该网站会使用新信息编辑页面。不幸的是,当页面更新时我能够访问一个事件是至关重要的。我主要尝试了很多方法

  • 在每次登录尝试时持续检查 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());
    }
}

正如上面cmets中的代码所述,我完全迷失了

MutationObserver mutationObserver = new MutationObserver(Gweb.Window.DomWindow, (nsISupports)Gweb.Document.DomObject);

我似乎无法在GeckoFxMutationObserver 源中找到任何可以让我设置回调/事件/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)*/);
    }
}

【问题讨论】:

    标签: c# geckofx mutation-observers


    【解决方案1】:

    我认为 Geckofx 的 webidl compiler 目前还不够先进,无法生成回调构造函数。

    选项 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 回调。

    (正如你已经提到的) - 这个例子一直等到调整大小完成。

    基本上注入:"&lt;body onresize=fireResizedEventAfterDelay()&gt;":然后注入这样的东西:

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

    【讨论】:

    • 您好,我正在尝试选项 2,但是,对于 var target = Xpcom.QueryInterface&lt;nsIDOMEventTarget&gt;(X);,我能找到的唯一合适的参考是 WindowRoot in Window.cs。然而,看起来虽然GeckoWindow.cs 扩展了Window.cs,但它并没有公开上述WindowRoot 属性。我会以不正确的方式解决这个问题吗?我已经在我原来的问题中包含了我尝试的相关部分。
    • var body = browser.Document?.Body?.DOMHtmlElement;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    相关资源
    最近更新 更多