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