2017-02-06 4 views
3

Я использую 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

Я не думаю, что 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. - Использование Idle + Check.

Добавить обработчик события 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);' был 'WindowRoot' в' Window.cs'. Однако, казалось бы, в то время как 'GeckoWindow.cs' расширяет' Window.cs', он не раскрывает вышеупомянутое свойство WindowRoot. Неужели я ошибаюсь? Я включил соответствующие части моей попытки в свой первоначальный вопрос. –

+1

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

+0

Ах, теперь я чувствую себя глупо ... спасибо Тому! –

Смежные вопросы