1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "WindowHook.h"
8 #include "nsWindow.h"
9 #include "nsWindowDefs.h"
10 
11 namespace mozilla {
12 namespace widget {
13 
AddHook(UINT nMsg,Callback callback,void * context)14 nsresult WindowHook::AddHook(UINT nMsg, Callback callback, void* context) {
15   MessageData* data = LookupOrCreate(nMsg);
16 
17   if (!data) return NS_ERROR_OUT_OF_MEMORY;
18 
19   // Ensure we don't overwrite another hook
20   NS_ENSURE_TRUE(nullptr == data->hook.cb, NS_ERROR_UNEXPECTED);
21 
22   data->hook = CallbackData(callback, context);
23 
24   return NS_OK;
25 }
26 
RemoveHook(UINT nMsg,Callback callback,void * context)27 nsresult WindowHook::RemoveHook(UINT nMsg, Callback callback, void* context) {
28   CallbackData cbdata(callback, context);
29   MessageData* data = Lookup(nMsg);
30   if (!data) return NS_ERROR_UNEXPECTED;
31   if (data->hook != cbdata) return NS_ERROR_UNEXPECTED;
32   data->hook = CallbackData();
33 
34   DeleteIfEmpty(data);
35   return NS_OK;
36 }
37 
AddMonitor(UINT nMsg,Callback callback,void * context)38 nsresult WindowHook::AddMonitor(UINT nMsg, Callback callback, void* context) {
39   MessageData* data = LookupOrCreate(nMsg);
40   return (data && data->monitors.AppendElement(CallbackData(callback, context),
41                                                fallible))
42              ? NS_OK
43              : NS_ERROR_OUT_OF_MEMORY;
44 }
45 
RemoveMonitor(UINT nMsg,Callback callback,void * context)46 nsresult WindowHook::RemoveMonitor(UINT nMsg, Callback callback,
47                                    void* context) {
48   CallbackData cbdata(callback, context);
49   MessageData* data = Lookup(nMsg);
50   if (!data) return NS_ERROR_UNEXPECTED;
51   CallbackDataArray::index_type idx = data->monitors.IndexOf(cbdata);
52   if (idx == CallbackDataArray::NoIndex) return NS_ERROR_UNEXPECTED;
53   data->monitors.RemoveElementAt(idx);
54   DeleteIfEmpty(data);
55   return NS_OK;
56 }
57 
Lookup(UINT nMsg)58 WindowHook::MessageData* WindowHook::Lookup(UINT nMsg) {
59   MessageDataArray::index_type idx;
60   for (idx = 0; idx < mMessageData.Length(); idx++) {
61     MessageData& data = mMessageData[idx];
62     if (data.nMsg == nMsg) return &data;
63   }
64   return nullptr;
65 }
66 
LookupOrCreate(UINT nMsg)67 WindowHook::MessageData* WindowHook::LookupOrCreate(UINT nMsg) {
68   MessageData* data = Lookup(nMsg);
69   if (!data) {
70     data = mMessageData.AppendElement();
71 
72     if (!data) return nullptr;
73 
74     data->nMsg = nMsg;
75   }
76   return data;
77 }
78 
DeleteIfEmpty(MessageData * data)79 void WindowHook::DeleteIfEmpty(MessageData* data) {
80   // Never remove a MessageData that has still a hook or monitor entries.
81   if (data->hook || !data->monitors.IsEmpty()) return;
82 
83   MessageDataArray::index_type idx;
84   idx = data - mMessageData.Elements();
85   NS_ASSERTION(
86       idx < mMessageData.Length(),
87       "Attempted to delete MessageData that doesn't belong to this array!");
88   mMessageData.RemoveElementAt(idx);
89 }
90 
Notify(HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam,MSGResult & aResult)91 bool WindowHook::Notify(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
92                         MSGResult& aResult) {
93   MessageData* data = Lookup(nMsg);
94   if (!data) return false;
95 
96   uint32_t length = data->monitors.Length();
97   for (uint32_t midx = 0; midx < length; midx++) {
98     data->monitors[midx].Invoke(hWnd, nMsg, wParam, lParam, &aResult.mResult);
99   }
100 
101   aResult.mConsumed =
102       data->hook.Invoke(hWnd, nMsg, wParam, lParam, &aResult.mResult);
103   return aResult.mConsumed;
104 }
105 
Invoke(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam,LRESULT * aResult)106 bool WindowHook::CallbackData::Invoke(HWND hWnd, UINT msg, WPARAM wParam,
107                                       LPARAM lParam, LRESULT* aResult) {
108   if (!cb) return false;
109   return cb(context, hWnd, msg, wParam, lParam, aResult);
110 }
111 
112 }  // namespace widget
113 }  // namespace mozilla
114