Internet Explorer 驱动程序内部原理
客户端代码进入驱动程序
我们使用 W3C WebDriver 协议与本地 HTTP 服务器实例通信。这大大简化了特定语言代码的实现,并最大限度地减少了必须使用本机代码互操作技术(如 JNA、ctypes、pinvoke 或 DL)调用的 C++ DLL 的入口点数量。
内存管理
IE 驱动程序利用活动模板库 (ATL) 来利用其对 COM 对象智能指针的实现。这使得 COM 对象的引用计数和清理更加容易。
为什么我们需要更改保护模式设置?
Windows Vista 上的 IE 7 引入了保护模式的概念,该模式在浏览时为底层 Windows 操作系统提供了一定的保护。问题在于,当您通过 COM 操作 IE 实例,并且导航到会导致进入或退出保护模式的页面时,IE 要求创建另一个浏览器会话。这将使先前会话的 COM 对象失效,不允许您再控制它。
在 IE 7 中,这通常会表现为一个新的顶层浏览器窗口;在 IE 8 中,将创建一个新的 IExplore.exe 进程,但它通常(并非总是!)无缝地将其附加到现有的 IE 顶层框架窗口。任何外部驱动 IE 的浏览器自动化框架(而不是使用 WebBrowser 控件)都会遇到这些问题。
为了解决这个问题,我们规定要使用 IE,所有区域都必须具有相同的保护模式设置。只要所有区域都启用或禁用,我们就可以防止转换为不同的保护模式区域,这将使我们的浏览器对象失效。它还允许用户继续在启用 UAC 的情况下运行,并且如果他们将所有区域的保护模式设置为“启用”,则可以在浏览器中安全运行。
在 IE 驱动程序的早期版本中,如果用户的保护模式设置不正确,我们会启动 IE,并且该进程会一直挂起,直到 HTTP 请求超时。这并非最佳选择,因为它没有指示需要设置什么。为了谨慎起见,我们不会修改用户的保护模式设置。但是,当前版本会检查保护模式设置是否正确设置,如果未正确设置,则会返回错误响应。
键盘和鼠标输入
关键文件:interactions.cpp
我们有两种方法可以模拟键盘和鼠标输入。第一种方法(在 webdriver 的某些部分使用)是在 DOM 上合成事件。这有许多缺点,因为每个浏览器(以及浏览器的每个版本)都有其自己独特的怪癖;要对这些进行建模是一项艰巨的任务,并且不可能完全正确(例如,很难判断 window.selection
应该是什么,并且这是某些浏览器上的只读属性)。另一种方法是在操作系统级别合成键盘和鼠标输入,理想情况下,不会从用户那里窃取焦点(只要长时间运行的 webdriver 测试运行,用户往往会在他们的计算机上做其他事情)。
执行此操作的代码位于 interactions.cpp。这里需要注意的关键是,我们使用 PostMessages 将窗口事件推送到 IE 实例的消息队列中。尤其值得注意的是输入:我们只发送“keydown”和“keyup”消息。“keypress”事件由 IE 的内部事件处理程序在必要时创建。由于并非总是生成按键事件(例如,并非每个字符都是可打印的,并且如果取消默认事件冒泡,则侦听器看不到按键事件),我们在按键按下后发送一个“探测”事件。一旦我们看到它已处理,我们就知道按键事件在要处理的事件堆栈上,并且发送按键抬起事件是安全的。如果不这样做,则可能会以错误的顺序触发事件,这绝对不是最佳的。
处理 InternetExplorerDriver
目前,所有语言(Java、C#、Python 和 Ruby)中都有针对 InternetExplorerDriver 运行的测试,因此无论您从客户端使用哪种语言,您都应该能够测试对本机代码的更改。要处理 C++ 代码,您需要 Visual Studio 2010 Professional 或更高版本。不幸的是,驱动程序的 C++ 代码使用 ATL 来缓解使用 COM 对象的痛苦,而 ATL 不提供 Visual C++ 2010 Express Edition。如果您使用 Eclipse,则进行和测试修改的过程如下
- 在 VS 中编辑 C++ 代码。
- 构建代码以确保其编译
- 准备好运行测试时,进行完整重建。这将导致创建的 DLL 被复制到正确的位置,以便在 Eclipse 中使用
- 加载 Eclipse(或其他 IDE,如 Idea)
- 编辑
SingleTestSuite
,使其为usingDriver(IE)
- 创建使用“webdriver-internet-explorer”项目的 JUnit 运行配置。如果不这样做,测试将根本无法工作,并且控制台上会出现一些神秘的错误消息。
完成基本设置后,您可以很快开始处理代码。您可以使用 Visual Studio(从“调试”菜单中,选择“附加到进程…”)附加到您从中执行代码的进程。