1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "FuzzingFunctions.h"
8 
9 #include "nsJSEnvironment.h"
10 #include "js/GCAPI.h"
11 #include "mozilla/dom/KeyboardEvent.h"
12 #include "mozilla/ErrorResult.h"
13 #include "mozilla/Sprintf.h"
14 #include "mozilla/TextEvents.h"
15 #include "mozilla/TextInputProcessor.h"
16 #include "nsFocusManager.h"
17 #include "nsIAccessibilityService.h"
18 #include "nsPIDOMWindow.h"
19 #include "xpcAccessibilityService.h"
20 
21 namespace mozilla {
22 namespace dom {
23 
24 /* static */
GarbageCollect(const GlobalObject &)25 void FuzzingFunctions::GarbageCollect(const GlobalObject&) {
26   nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS,
27                                  nsJSContext::NonShrinkingGC);
28 }
29 
30 /* static */
GarbageCollectCompacting(const GlobalObject &)31 void FuzzingFunctions::GarbageCollectCompacting(const GlobalObject&) {
32   nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS,
33                                  nsJSContext::ShrinkingGC);
34 }
35 
36 /* static */
Crash(const GlobalObject & aGlobalObject,const nsAString & aKeyValue)37 void FuzzingFunctions::Crash(const GlobalObject& aGlobalObject,
38                              const nsAString& aKeyValue) {
39   char msgbuf[250];
40 
41   SprintfLiteral(msgbuf, "%s", NS_ConvertUTF16toUTF8(aKeyValue).get());
42   if (aKeyValue.Length() >= sizeof(msgbuf)) {
43     // Update the end of a truncated message to '...'.
44     strcpy(&msgbuf[sizeof(msgbuf) - 4], "...");
45   }
46   MOZ_CRASH_UNSAFE_PRINTF("%s", msgbuf);
47 }
48 
49 /* static */
CycleCollect(const GlobalObject &)50 void FuzzingFunctions::CycleCollect(const GlobalObject&) {
51   nsJSContext::CycleCollectNow(CCReason::API);
52 }
53 
MemoryPressure(const GlobalObject &)54 void FuzzingFunctions::MemoryPressure(const GlobalObject&) {
55   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
56   os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
57 }
58 
59 /* static */
EnableAccessibility(const GlobalObject &,ErrorResult & aRv)60 void FuzzingFunctions::EnableAccessibility(const GlobalObject&,
61                                            ErrorResult& aRv) {
62   RefPtr<nsIAccessibilityService> a11y;
63   nsresult rv;
64 
65   rv = NS_GetAccessibilityService(getter_AddRefs(a11y));
66   if (NS_FAILED(rv)) {
67     aRv.Throw(rv);
68   }
69 }
70 
71 struct ModifierKey final {
72   Modifier mModifier;
73   KeyNameIndex mKeyNameIndex;
74   bool mLockable;
75 
ModifierKeymozilla::dom::ModifierKey76   ModifierKey(Modifier aModifier, KeyNameIndex aKeyNameIndex, bool aLockable)
77       : mModifier(aModifier),
78         mKeyNameIndex(aKeyNameIndex),
79         mLockable(aLockable) {}
80 };
81 
82 static const ModifierKey kModifierKeys[] = {
83     ModifierKey(MODIFIER_ALT, KEY_NAME_INDEX_Alt, false),
84     ModifierKey(MODIFIER_ALTGRAPH, KEY_NAME_INDEX_AltGraph, false),
85     ModifierKey(MODIFIER_CONTROL, KEY_NAME_INDEX_Control, false),
86     ModifierKey(MODIFIER_FN, KEY_NAME_INDEX_Fn, false),
87     ModifierKey(MODIFIER_META, KEY_NAME_INDEX_Meta, false),
88     ModifierKey(MODIFIER_OS, KEY_NAME_INDEX_OS, false),
89     ModifierKey(MODIFIER_SHIFT, KEY_NAME_INDEX_Shift, false),
90     ModifierKey(MODIFIER_SYMBOL, KEY_NAME_INDEX_Symbol, false),
91     ModifierKey(MODIFIER_CAPSLOCK, KEY_NAME_INDEX_CapsLock, true),
92     ModifierKey(MODIFIER_FNLOCK, KEY_NAME_INDEX_FnLock, true),
93     ModifierKey(MODIFIER_NUMLOCK, KEY_NAME_INDEX_NumLock, true),
94     ModifierKey(MODIFIER_SCROLLLOCK, KEY_NAME_INDEX_ScrollLock, true),
95     ModifierKey(MODIFIER_SYMBOLLOCK, KEY_NAME_INDEX_SymbolLock, true),
96 };
97 
98 /* static */
ActivateModifiers(TextInputProcessor * aTextInputProcessor,Modifiers aModifiers,nsIWidget * aWidget,ErrorResult & aRv)99 Modifiers FuzzingFunctions::ActivateModifiers(
100     TextInputProcessor* aTextInputProcessor, Modifiers aModifiers,
101     nsIWidget* aWidget, ErrorResult& aRv) {
102   MOZ_ASSERT(aTextInputProcessor);
103 
104   if (aModifiers == MODIFIER_NONE) {
105     return MODIFIER_NONE;
106   }
107 
108   // We don't want to dispatch modifier key event from here.  In strictly
109   // speaking, all necessary modifiers should be activated with dispatching
110   // each modifier key event.  However, we cannot keep storing
111   // TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
112   // So, if some callers need to emulate modifier key events, they should do
113   // it by themselves.
114   uint32_t flags = nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
115                    nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
116 
117   Modifiers activatedModifiers = MODIFIER_NONE;
118   Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
119   for (const ModifierKey& kModifierKey : kModifierKeys) {
120     if (!(kModifierKey.mModifier & aModifiers)) {
121       continue;  // Not requested modifier.
122     }
123     if (kModifierKey.mModifier & activeModifiers) {
124       continue;  // Already active, do nothing.
125     }
126     WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
127     // mKeyCode will be computed by TextInputProcessor automatically.
128     event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
129     aRv = aTextInputProcessor->Keydown(event, flags);
130     if (NS_WARN_IF(aRv.Failed())) {
131       return activatedModifiers;
132     }
133     if (kModifierKey.mLockable) {
134       aRv = aTextInputProcessor->Keyup(event, flags);
135       if (NS_WARN_IF(aRv.Failed())) {
136         return activatedModifiers;
137       }
138     }
139     activatedModifiers |= kModifierKey.mModifier;
140   }
141   return activatedModifiers;
142 }
143 
144 /* static */
InactivateModifiers(TextInputProcessor * aTextInputProcessor,Modifiers aModifiers,nsIWidget * aWidget,ErrorResult & aRv)145 Modifiers FuzzingFunctions::InactivateModifiers(
146     TextInputProcessor* aTextInputProcessor, Modifiers aModifiers,
147     nsIWidget* aWidget, ErrorResult& aRv) {
148   MOZ_ASSERT(aTextInputProcessor);
149 
150   if (aModifiers == MODIFIER_NONE) {
151     return MODIFIER_NONE;
152   }
153 
154   // We don't want to dispatch modifier key event from here.  In strictly
155   // speaking, all necessary modifiers should be activated with dispatching
156   // each modifier key event.  However, we cannot keep storing
157   // TextInputProcessor instance for multiple SynthesizeKeyboardEvents() calls.
158   // So, if some callers need to emulate modifier key events, they should do
159   // it by themselves.
160   uint32_t flags = nsITextInputProcessor::KEY_NON_PRINTABLE_KEY |
161                    nsITextInputProcessor::KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT;
162 
163   Modifiers inactivatedModifiers = MODIFIER_NONE;
164   Modifiers activeModifiers = aTextInputProcessor->GetActiveModifiers();
165   for (const ModifierKey& kModifierKey : kModifierKeys) {
166     if (!(kModifierKey.mModifier & aModifiers)) {
167       continue;  // Not requested modifier.
168     }
169     if (kModifierKey.mModifier & activeModifiers) {
170       continue;  // Already active, do nothing.
171     }
172     WidgetKeyboardEvent event(true, eVoidEvent, aWidget);
173     // mKeyCode will be computed by TextInputProcessor automatically.
174     event.mKeyNameIndex = kModifierKey.mKeyNameIndex;
175     if (kModifierKey.mLockable) {
176       aRv = aTextInputProcessor->Keydown(event, flags);
177       if (NS_WARN_IF(aRv.Failed())) {
178         return inactivatedModifiers;
179       }
180     }
181     aRv = aTextInputProcessor->Keyup(event, flags);
182     if (NS_WARN_IF(aRv.Failed())) {
183       return inactivatedModifiers;
184     }
185     inactivatedModifiers |= kModifierKey.mModifier;
186   }
187   return inactivatedModifiers;
188 }
189 
190 /* static */
SynthesizeKeyboardEvents(const GlobalObject & aGlobalObject,const nsAString & aKeyValue,const KeyboardEventInit & aDict,ErrorResult & aRv)191 void FuzzingFunctions::SynthesizeKeyboardEvents(
192     const GlobalObject& aGlobalObject, const nsAString& aKeyValue,
193     const KeyboardEventInit& aDict, ErrorResult& aRv) {
194   // Prepare keyboard event to synthesize first.
195   uint32_t flags = 0;
196   // Don't modify the given dictionary since caller may want to modify
197   // a part of it and call this with it again.
198   WidgetKeyboardEvent event(true, eVoidEvent, nullptr);
199   event.mKeyCode = aDict.mKeyCode;
200   event.mCharCode = 0;  // Ignore.
201   event.mKeyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue);
202   if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
203     event.mKeyValue = aKeyValue;
204   }
205   // code value should be empty string or one of valid code value.
206   event.mCodeNameIndex =
207       aDict.mCode.IsEmpty()
208           ? CODE_NAME_INDEX_UNKNOWN
209           : WidgetKeyboardEvent::GetCodeNameIndex(aDict.mCode);
210   if (NS_WARN_IF(event.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
211     // Meaning that the code value is specified but it's not a known code
212     // value.  TextInputProcessor does not support synthesizing keyboard
213     // events with unknown code value.  So, returns error now.
214     aRv.Throw(NS_ERROR_INVALID_ARG);
215     return;
216   }
217   event.mLocation = aDict.mLocation;
218   event.mIsRepeat = aDict.mRepeat;
219 
220 #define SET_MODIFIER(aName, aValue) \
221   if (aDict.m##aName) {             \
222     event.mModifiers |= aValue;     \
223   }
224 
225   SET_MODIFIER(CtrlKey, MODIFIER_CONTROL)
226   SET_MODIFIER(ShiftKey, MODIFIER_SHIFT)
227   SET_MODIFIER(AltKey, MODIFIER_ALT)
228   SET_MODIFIER(MetaKey, MODIFIER_META)
229   SET_MODIFIER(ModifierAltGraph, MODIFIER_ALTGRAPH)
230   SET_MODIFIER(ModifierCapsLock, MODIFIER_CAPSLOCK)
231   SET_MODIFIER(ModifierFn, MODIFIER_FN)
232   SET_MODIFIER(ModifierFnLock, MODIFIER_FNLOCK)
233   SET_MODIFIER(ModifierNumLock, MODIFIER_NUMLOCK)
234   SET_MODIFIER(ModifierOS, MODIFIER_OS)
235   SET_MODIFIER(ModifierScrollLock, MODIFIER_SCROLLLOCK)
236   SET_MODIFIER(ModifierSymbol, MODIFIER_SYMBOL)
237   SET_MODIFIER(ModifierSymbolLock, MODIFIER_SYMBOLLOCK)
238 
239 #undef SET_MODIFIER
240 
241   // If we could distinguish whether the caller specified 0 explicitly or
242   // not, we would skip computing the key location when it's specified
243   // explicitly.  However, this caller probably won't test tricky keyboard
244   // events, so, it must be enough even though caller cannot set location
245   // to 0.
246   Maybe<uint32_t> maybeNonStandardLocation;
247   if (!event.mLocation) {
248     maybeNonStandardLocation = mozilla::Some(event.mLocation);
249   }
250 
251   // If the key is a printable key and |.code| and/or |.keyCode| value is
252   // not specified as non-zero explicitly, let's assume that the caller
253   // emulates US-English keyboard's behavior (because otherwise, caller
254   // should set both values.
255   if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
256     if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
257       event.mCodeNameIndex =
258           TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
259               event.mKeyValue, maybeNonStandardLocation);
260       MOZ_ASSERT(event.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
261     }
262     if (!event.mKeyCode) {
263       event.mKeyCode =
264           TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
265               event.mKeyValue, maybeNonStandardLocation);
266       if (!event.mKeyCode) {
267         // Prevent to recompute keyCode in TextInputProcessor.
268         flags |= nsITextInputProcessor::KEY_KEEP_KEYCODE_ZERO;
269       }
270     }
271   }
272   // If the key is a non-printable key, we can compute |.code| value of
273   // usual keyboard of the platform.  Note that |.keyCode| value for
274   // non-printable key will be computed by TextInputProcessor.  So, we need
275   // to take care only |.code| value here.
276   else if (event.mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
277     event.mCodeNameIndex =
278         WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(
279             event.mKeyNameIndex, maybeNonStandardLocation);
280   }
281 
282   // Synthesize keyboard events in a DOM window which is in-process top one.
283   // For emulating user input, this is better than dispatching the events in
284   // the caller's DOM window because this approach can test the path redirecting
285   // the events to focused subdocument too.  However, for now, we cannot
286   // dispatch it via another process without big changes.  Therefore, we should
287   // use in-process top window instead.  If you need to test the path in the
288   // parent process to, please file a feature request bug.
289   nsCOMPtr<nsPIDOMWindowInner> windowInner =
290       do_QueryInterface(aGlobalObject.GetAsSupports());
291   if (!windowInner) {
292     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
293     return;
294   }
295 
296   nsPIDOMWindowOuter* inProcessTopWindowOuter =
297       windowInner->GetInProcessScriptableTop();
298   if (NS_WARN_IF(!inProcessTopWindowOuter)) {
299     aRv.Throw(NS_ERROR_FAILURE);
300     return;
301   }
302 
303   nsIDocShell* docShell = inProcessTopWindowOuter->GetDocShell();
304   if (NS_WARN_IF(!docShell)) {
305     aRv.Throw(NS_ERROR_FAILURE);
306     return;
307   }
308 
309   RefPtr<nsPresContext> presContext = docShell->GetPresContext();
310   if (NS_WARN_IF(!presContext)) {
311     aRv.Throw(NS_ERROR_FAILURE);
312     return;
313   }
314 
315   event.mWidget = presContext->GetRootWidget();
316   if (NS_WARN_IF(!event.mWidget)) {
317     aRv.Throw(NS_ERROR_FAILURE);
318     return;
319   }
320 
321   nsCOMPtr<nsPIDOMWindowInner> inProcessTopWindowInner =
322       inProcessTopWindowOuter->EnsureInnerWindow();
323   if (NS_WARN_IF(!inProcessTopWindowInner)) {
324     aRv.Throw(NS_ERROR_FAILURE);
325     return;
326   }
327 
328   RefPtr<TextInputProcessor> textInputProcessor = new TextInputProcessor();
329   bool beganInputTransaction = false;
330   aRv = textInputProcessor->BeginInputTransactionForFuzzing(
331       inProcessTopWindowInner, nullptr, &beganInputTransaction);
332   if (NS_WARN_IF(aRv.Failed())) {
333     return;
334   }
335   if (NS_WARN_IF(!beganInputTransaction)) {
336     // This is possible if a keyboard event listener or something tries to
337     // dispatch next keyboard events during dispatching a keyboard event via
338     // TextInputProcessor.
339     aRv.Throw(NS_ERROR_FAILURE);
340     return;
341   }
342 
343   // First, activate necessary modifiers.
344   // MOZ_KnownLive(event.mWidget) is safe because `event` is an instance in
345   // the stack, and `mWidget` is `nsCOMPtr<nsIWidget>`.
346   Modifiers activatedModifiers = ActivateModifiers(
347       textInputProcessor, event.mModifiers, MOZ_KnownLive(event.mWidget), aRv);
348   if (NS_WARN_IF(aRv.Failed())) {
349     return;
350   }
351 
352   // Then, dispatch keydown and keypress.
353   aRv = textInputProcessor->Keydown(event, flags);
354   if (NS_WARN_IF(aRv.Failed())) {
355     return;
356   }
357 
358   // Then, dispatch keyup.
359   aRv = textInputProcessor->Keyup(event, flags);
360   if (NS_WARN_IF(aRv.Failed())) {
361     return;
362   }
363 
364   // Finally, inactivate some modifiers which are activated by this call.
365   // MOZ_KnownLive(event.mWidget) is safe because `event` is an instance in
366   // the stack, and `mWidget` is `nsCOMPtr<nsIWidget>`.
367   InactivateModifiers(textInputProcessor, activatedModifiers,
368                       MOZ_KnownLive(event.mWidget), aRv);
369   if (NS_WARN_IF(aRv.Failed())) {
370     return;
371   }
372 
373   // Unfortunately, we cannot keep storing modifier state in the
374   // TextInputProcessor since if we store it into a static variable,
375   // we need to take care of resetting it when the caller wants.
376   // However, that makes API more complicated.  So, until they need
377   // to want
378 }
379 
380 }  // namespace dom
381 }  // namespace mozilla
382