diff options
Diffstat (limited to 'dom/plugins/test/mochitest/dialog_watcher.js')
-rw-r--r-- | dom/plugins/test/mochitest/dialog_watcher.js | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/dom/plugins/test/mochitest/dialog_watcher.js b/dom/plugins/test/mochitest/dialog_watcher.js new file mode 100644 index 000000000..8a8c82997 --- /dev/null +++ b/dom/plugins/test/mochitest/dialog_watcher.js @@ -0,0 +1,183 @@ +const EVENT_OBJECT_SHOW = 0x8002; +const EVENT_OBJECT_HIDE = 0x8003; +const WINEVENT_OUTOFCONTEXT = 0; +const WINEVENT_SKIPOWNPROCESS = 2; +const QS_ALLINPUT = 0x04FF; +const INFINITE = 0xFFFFFFFF; +const WAIT_OBJECT_0 = 0; +const WAIT_TIMEOUT = 258; +const PM_NOREMOVE = 0; + +function DialogWatcher(titleText, onDialogStart, onDialogEnd) { + this.titleText = titleText; + this.onDialogStart = onDialogStart; + this.onDialogEnd = onDialogEnd; +} + +DialogWatcher.prototype.init = function() { + this.hwnd = undefined; + if (!this.user32) { + this.user32 = ctypes.open("user32.dll"); + } + if (!this.findWindow) { + this.findWindow = user32.declare("FindWindowW", + ctypes.winapi_abi, + ctypes.uintptr_t, + ctypes.char16_t.ptr, + ctypes.char16_t.ptr); + } + if (!this.winEventProcType) { + this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi, + ctypes.void_t, + [ctypes.uintptr_t, + ctypes.uint32_t, + ctypes.uintptr_t, + ctypes.long, + ctypes.long, + ctypes.uint32_t, + ctypes.uint32_t]).ptr; + } + if (!this.setWinEventHook) { + this.setWinEventHook = user32.declare("SetWinEventHook", + ctypes.winapi_abi, + ctypes.uintptr_t, + ctypes.uint32_t, + ctypes.uint32_t, + ctypes.uintptr_t, + this.winEventProcType, + ctypes.uint32_t, + ctypes.uint32_t, + ctypes.uint32_t); + } + if (!this.unhookWinEvent) { + this.unhookWinEvent = user32.declare("UnhookWinEvent", + ctypes.winapi_abi, + ctypes.int, + ctypes.uintptr_t); + } + if (!this.pointType) { + this.pointType = ctypes.StructType("tagPOINT", + [ { "x": ctypes.long }, + { "y": ctypes.long } ] ); + } + if (!this.msgType) { + this.msgType = ctypes.StructType("tagMSG", + [ { "hwnd": ctypes.uintptr_t }, + { "message": ctypes.uint32_t }, + { "wParam": ctypes.uintptr_t }, + { "lParam": ctypes.intptr_t }, + { "time": ctypes.uint32_t }, + { "pt": this.pointType } ] ); + } + if (!this.peekMessage) { + this.peekMessage = user32.declare("PeekMessageW", + ctypes.winapi_abi, + ctypes.int, + this.msgType.ptr, + ctypes.uintptr_t, + ctypes.uint32_t, + ctypes.uint32_t, + ctypes.uint32_t); + } + if (!this.msgWaitForMultipleObjects) { + this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects", + ctypes.winapi_abi, + ctypes.uint32_t, + ctypes.uint32_t, + ctypes.uintptr_t.ptr, + ctypes.int, + ctypes.uint32_t, + ctypes.uint32_t); + } + if (!this.getWindowTextW) { + this.getWindowTextW = user32.declare("GetWindowTextW", + ctypes.winapi_abi, + ctypes.int, + ctypes.uintptr_t, + ctypes.char16_t.ptr, + ctypes.int); + } + if (!this.messageBox) { + // Handy for debugging this code + this.messageBox = user32.declare("MessageBoxW", + ctypes.winapi_abi, + ctypes.int, + ctypes.uintptr_t, + ctypes.char16_t.ptr, + ctypes.char16_t.ptr, + ctypes.uint32_t); + } +}; + +DialogWatcher.prototype.getWindowText = function(hwnd) { + var bufType = ctypes.ArrayType(ctypes.char16_t); + var buffer = new bufType(256); + + if (this.getWindowTextW(hwnd, buffer, buffer.length)) { + return buffer.readString(); + } +}; + +DialogWatcher.prototype.processWindowEvents = function(timeout) { + var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) { + var nhwnd = Number(hwnd) + if (event == EVENT_OBJECT_SHOW) { + if (nhwnd == self.hwnd) { + // We've already picked up this event via FindWindow + return; + } + var windowText = self.getWindowText(hwnd); + if (windowText == self.titleText && self.onDialogStart) { + self.hwnd = nhwnd; + self.onDialogStart(nhwnd); + } + } else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) { + self.onDialogEnd(); + self.hwnd = null; + } + }; + var self = this; + var callback = this.winEventProcType(function(hook, event, hwnd, idObject, + idChild, dwEventThread, + dwmsEventTime) { + onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread, + dwmsEventTime); + } ); + var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, + 0, callback, 0, 0, + WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); + if (!hook) { + return; + } + // Check if the window is already showing + var hwnd = this.findWindow(null, this.titleText); + if (hwnd && hwnd > 0) { + this.hwnd = Number(hwnd); + if (this.onDialogStart) { + this.onDialogStart(this.hwnd); + } + } + + if (!timeout) { + timeout = INFINITE; + } + + var waitStatus = WAIT_OBJECT_0; + var expectingStart = this.onDialogStart && this.hwnd === undefined; + var startWaitTime = Date.now(); + while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) { + waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ? + INFINITE : timeout, 0); + if (waitStatus == WAIT_OBJECT_0) { + var msg = new this.msgType; + this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE); + } + if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) { + break; + } + } + + this.unhookWinEvent(hook); + // Returns true if the hook was successful, something was found, and we never timed out + return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0; +}; |