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