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"
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"
25 namespace mozilla::dom
{
28 void FuzzingFunctions::GarbageCollect(const GlobalObject
&) {
29 nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS
,
30 nsJSContext::NonShrinkingGC
);
34 void FuzzingFunctions::GarbageCollectCompacting(const GlobalObject
&) {
35 nsJSContext::GarbageCollectNow(JS::GCReason::COMPONENT_UTILS
,
36 nsJSContext::ShrinkingGC
);
40 void FuzzingFunctions::Crash(const GlobalObject
& aGlobalObject
,
41 const nsAString
& aKeyValue
) {
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
);
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");
63 void FuzzingFunctions::SignalIPCReady(const GlobalObject
&) {
64 #ifdef FUZZING_SNAPSHOT
65 ContentChild::GetSingleton()->SendSignalFuzzingReady();
70 void FuzzingFunctions::EnableAccessibility(const GlobalObject
&,
72 RefPtr
<nsIAccessibilityService
> a11y
;
75 rv
= NS_GetAccessibilityService(getter_AddRefs(a11y
));
81 struct ModifierKey final
{
83 KeyNameIndex mKeyNameIndex
;
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),
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
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
;
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
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
;
200 void FuzzingFunctions::SynthesizeKeyboardEvents(
201 const GlobalObject
& aGlobalObject
, const nsAString
& aKeyValue
,
202 const KeyboardEventInit
& aDict
, ErrorResult
& aRv
) {
203 // Prepare keyboard event to synthesize first.
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
);
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
)
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
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
) {
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());
300 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
304 nsPIDOMWindowOuter
* inProcessTopWindowOuter
=
305 windowInner
->GetInProcessScriptableTop();
306 if (NS_WARN_IF(!inProcessTopWindowOuter
)) {
307 aRv
.Throw(NS_ERROR_FAILURE
);
311 nsIDocShell
* docShell
= inProcessTopWindowOuter
->GetDocShell();
312 if (NS_WARN_IF(!docShell
)) {
313 aRv
.Throw(NS_ERROR_FAILURE
);
317 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
318 if (NS_WARN_IF(!presContext
)) {
319 aRv
.Throw(NS_ERROR_FAILURE
);
323 event
.mWidget
= presContext
->GetRootWidget();
324 if (NS_WARN_IF(!event
.mWidget
)) {
325 aRv
.Throw(NS_ERROR_FAILURE
);
329 nsCOMPtr
<nsPIDOMWindowInner
> inProcessTopWindowInner
=
330 inProcessTopWindowOuter
->EnsureInnerWindow();
331 if (NS_WARN_IF(!inProcessTopWindowInner
)) {
332 aRv
.Throw(NS_ERROR_FAILURE
);
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())) {
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
);
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())) {
360 // Then, dispatch keydown and keypress.
361 aRv
= textInputProcessor
->Keydown(event
, flags
);
362 if (NS_WARN_IF(aRv
.Failed())) {
366 // Then, dispatch keyup.
367 aRv
= textInputProcessor
->Keyup(event
, flags
);
368 if (NS_WARN_IF(aRv
.Failed())) {
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())) {
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
388 } // namespace mozilla::dom