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