Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | const EVENT_OBJECT_SHOW = 0x8002; |
michael@0 | 2 | const EVENT_OBJECT_HIDE = 0x8003; |
michael@0 | 3 | const WINEVENT_OUTOFCONTEXT = 0; |
michael@0 | 4 | const WINEVENT_SKIPOWNPROCESS = 2; |
michael@0 | 5 | const QS_ALLINPUT = 0x04FF; |
michael@0 | 6 | const INFINITE = 0xFFFFFFFF; |
michael@0 | 7 | const WAIT_OBJECT_0 = 0; |
michael@0 | 8 | const WAIT_TIMEOUT = 258; |
michael@0 | 9 | const PM_NOREMOVE = 0; |
michael@0 | 10 | |
michael@0 | 11 | function DialogWatcher(titleText, onDialogStart, onDialogEnd) { |
michael@0 | 12 | this.titleText = titleText; |
michael@0 | 13 | this.onDialogStart = onDialogStart; |
michael@0 | 14 | this.onDialogEnd = onDialogEnd; |
michael@0 | 15 | } |
michael@0 | 16 | |
michael@0 | 17 | DialogWatcher.prototype.init = function() { |
michael@0 | 18 | this.hwnd = undefined; |
michael@0 | 19 | if (!this.user32) { |
michael@0 | 20 | this.user32 = ctypes.open("user32.dll"); |
michael@0 | 21 | } |
michael@0 | 22 | if (!this.findWindow) { |
michael@0 | 23 | this.findWindow = user32.declare("FindWindowW", |
michael@0 | 24 | ctypes.winapi_abi, |
michael@0 | 25 | ctypes.uintptr_t, |
michael@0 | 26 | ctypes.jschar.ptr, |
michael@0 | 27 | ctypes.jschar.ptr); |
michael@0 | 28 | } |
michael@0 | 29 | if (!this.winEventProcType) { |
michael@0 | 30 | this.winEventProcType = ctypes.FunctionType(ctypes.stdcall_abi, |
michael@0 | 31 | ctypes.void_t, |
michael@0 | 32 | [ctypes.uintptr_t, |
michael@0 | 33 | ctypes.uint32_t, |
michael@0 | 34 | ctypes.uintptr_t, |
michael@0 | 35 | ctypes.long, |
michael@0 | 36 | ctypes.long, |
michael@0 | 37 | ctypes.uint32_t, |
michael@0 | 38 | ctypes.uint32_t]).ptr; |
michael@0 | 39 | } |
michael@0 | 40 | if (!this.setWinEventHook) { |
michael@0 | 41 | this.setWinEventHook = user32.declare("SetWinEventHook", |
michael@0 | 42 | ctypes.winapi_abi, |
michael@0 | 43 | ctypes.uintptr_t, |
michael@0 | 44 | ctypes.uint32_t, |
michael@0 | 45 | ctypes.uint32_t, |
michael@0 | 46 | ctypes.uintptr_t, |
michael@0 | 47 | this.winEventProcType, |
michael@0 | 48 | ctypes.uint32_t, |
michael@0 | 49 | ctypes.uint32_t, |
michael@0 | 50 | ctypes.uint32_t); |
michael@0 | 51 | } |
michael@0 | 52 | if (!this.unhookWinEvent) { |
michael@0 | 53 | this.unhookWinEvent = user32.declare("UnhookWinEvent", |
michael@0 | 54 | ctypes.winapi_abi, |
michael@0 | 55 | ctypes.int, |
michael@0 | 56 | ctypes.uintptr_t); |
michael@0 | 57 | } |
michael@0 | 58 | if (!this.pointType) { |
michael@0 | 59 | this.pointType = ctypes.StructType("tagPOINT", |
michael@0 | 60 | [ { "x": ctypes.long }, |
michael@0 | 61 | { "y": ctypes.long } ] ); |
michael@0 | 62 | } |
michael@0 | 63 | if (!this.msgType) { |
michael@0 | 64 | this.msgType = ctypes.StructType("tagMSG", |
michael@0 | 65 | [ { "hwnd": ctypes.uintptr_t }, |
michael@0 | 66 | { "message": ctypes.uint32_t }, |
michael@0 | 67 | { "wParam": ctypes.uintptr_t }, |
michael@0 | 68 | { "lParam": ctypes.intptr_t }, |
michael@0 | 69 | { "time": ctypes.uint32_t }, |
michael@0 | 70 | { "pt": this.pointType } ] ); |
michael@0 | 71 | } |
michael@0 | 72 | if (!this.peekMessage) { |
michael@0 | 73 | this.peekMessage = user32.declare("PeekMessageW", |
michael@0 | 74 | ctypes.winapi_abi, |
michael@0 | 75 | ctypes.int, |
michael@0 | 76 | this.msgType.ptr, |
michael@0 | 77 | ctypes.uintptr_t, |
michael@0 | 78 | ctypes.uint32_t, |
michael@0 | 79 | ctypes.uint32_t, |
michael@0 | 80 | ctypes.uint32_t); |
michael@0 | 81 | } |
michael@0 | 82 | if (!this.msgWaitForMultipleObjects) { |
michael@0 | 83 | this.msgWaitForMultipleObjects = user32.declare("MsgWaitForMultipleObjects", |
michael@0 | 84 | ctypes.winapi_abi, |
michael@0 | 85 | ctypes.uint32_t, |
michael@0 | 86 | ctypes.uint32_t, |
michael@0 | 87 | ctypes.uintptr_t.ptr, |
michael@0 | 88 | ctypes.int, |
michael@0 | 89 | ctypes.uint32_t, |
michael@0 | 90 | ctypes.uint32_t); |
michael@0 | 91 | } |
michael@0 | 92 | if (!this.getWindowTextW) { |
michael@0 | 93 | this.getWindowTextW = user32.declare("GetWindowTextW", |
michael@0 | 94 | ctypes.winapi_abi, |
michael@0 | 95 | ctypes.int, |
michael@0 | 96 | ctypes.uintptr_t, |
michael@0 | 97 | ctypes.jschar.ptr, |
michael@0 | 98 | ctypes.int); |
michael@0 | 99 | } |
michael@0 | 100 | if (!this.messageBox) { |
michael@0 | 101 | // Handy for debugging this code |
michael@0 | 102 | this.messageBox = user32.declare("MessageBoxW", |
michael@0 | 103 | ctypes.winapi_abi, |
michael@0 | 104 | ctypes.int, |
michael@0 | 105 | ctypes.uintptr_t, |
michael@0 | 106 | ctypes.jschar.ptr, |
michael@0 | 107 | ctypes.jschar.ptr, |
michael@0 | 108 | ctypes.uint32_t); |
michael@0 | 109 | } |
michael@0 | 110 | }; |
michael@0 | 111 | |
michael@0 | 112 | DialogWatcher.prototype.getWindowText = function(hwnd) { |
michael@0 | 113 | var bufType = ctypes.ArrayType(ctypes.jschar); |
michael@0 | 114 | var buffer = new bufType(256); |
michael@0 | 115 | |
michael@0 | 116 | if (this.getWindowTextW(hwnd, buffer, buffer.length)) { |
michael@0 | 117 | return buffer.readString(); |
michael@0 | 118 | } |
michael@0 | 119 | }; |
michael@0 | 120 | |
michael@0 | 121 | DialogWatcher.prototype.processWindowEvents = function(timeout) { |
michael@0 | 122 | var onWinEvent = function(self, hook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime) { |
michael@0 | 123 | var nhwnd = Number(hwnd) |
michael@0 | 124 | if (event == EVENT_OBJECT_SHOW) { |
michael@0 | 125 | if (nhwnd == self.hwnd) { |
michael@0 | 126 | // We've already picked up this event via FindWindow |
michael@0 | 127 | return; |
michael@0 | 128 | } |
michael@0 | 129 | var windowText = self.getWindowText(hwnd); |
michael@0 | 130 | if (windowText == self.titleText && self.onDialogStart) { |
michael@0 | 131 | self.hwnd = nhwnd; |
michael@0 | 132 | self.onDialogStart(nhwnd); |
michael@0 | 133 | } |
michael@0 | 134 | } else if (event == EVENT_OBJECT_HIDE && nhwnd == self.hwnd && self.onDialogEnd) { |
michael@0 | 135 | self.onDialogEnd(); |
michael@0 | 136 | self.hwnd = null; |
michael@0 | 137 | } |
michael@0 | 138 | }; |
michael@0 | 139 | var self = this; |
michael@0 | 140 | var callback = this.winEventProcType(function(hook, event, hwnd, idObject, |
michael@0 | 141 | idChild, dwEventThread, |
michael@0 | 142 | dwmsEventTime) { |
michael@0 | 143 | onWinEvent(self, hook, event, hwnd, idObject, idChild, dwEventThread, |
michael@0 | 144 | dwmsEventTime); |
michael@0 | 145 | } ); |
michael@0 | 146 | var hook = this.setWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE, |
michael@0 | 147 | 0, callback, 0, 0, |
michael@0 | 148 | WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); |
michael@0 | 149 | if (!hook) { |
michael@0 | 150 | return; |
michael@0 | 151 | } |
michael@0 | 152 | // Check if the window is already showing |
michael@0 | 153 | var hwnd = this.findWindow(null, this.titleText); |
michael@0 | 154 | if (hwnd && hwnd > 0) { |
michael@0 | 155 | this.hwnd = Number(hwnd); |
michael@0 | 156 | if (this.onDialogStart) { |
michael@0 | 157 | this.onDialogStart(this.hwnd); |
michael@0 | 158 | } |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | if (!timeout) { |
michael@0 | 162 | timeout = INFINITE; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | var waitStatus = WAIT_OBJECT_0; |
michael@0 | 166 | var expectingStart = this.onDialogStart && this.hwnd === undefined; |
michael@0 | 167 | var startWaitTime = Date.now(); |
michael@0 | 168 | while (this.hwnd === undefined || this.onDialogEnd && this.hwnd) { |
michael@0 | 169 | waitStatus = this.msgWaitForMultipleObjects(0, null, 0, expectingStart ? |
michael@0 | 170 | INFINITE : timeout, 0); |
michael@0 | 171 | if (waitStatus == WAIT_OBJECT_0) { |
michael@0 | 172 | var msg = new this.msgType; |
michael@0 | 173 | this.peekMessage(msg.address(), 0, 0, 0, PM_NOREMOVE); |
michael@0 | 174 | } |
michael@0 | 175 | if (waitStatus == WAIT_TIMEOUT || (Date.now() - startWaitTime) >= timeout) { |
michael@0 | 176 | break; |
michael@0 | 177 | } |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | this.unhookWinEvent(hook); |
michael@0 | 181 | // Returns true if the hook was successful, something was found, and we never timed out |
michael@0 | 182 | return this.hwnd !== undefined && waitStatus == WAIT_OBJECT_0; |
michael@0 | 183 | }; |
michael@0 | 184 |