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