Bug 1222285 - Part 1: Spoofing the keyboard event to mimc a certain keyboard layout according to the content-language of the document when 'privacy.resistFingerprinting' is true. r?masayuki,arthuredelstein,smaug draft
authorTim Huang <tihuang@mozilla.com>
Tue, 29 Aug 2017 11:33:27 +0800
changeset 720798 bec2a7fde9ea2508dd874106cb494453d06fbd50
parent 720739 a3887394965f161d011eebc74e8987a653366e4b
child 720799 d74e47db32dbb2bfb0e22728ad788494b72d343e
push id95644
push userbmo:tihuang@mozilla.com
push dateTue, 16 Jan 2018 09:13:15 +0000
reviewersmasayuki, arthuredelstein, smaug
bugs1222285
milestone59.0a1
Bug 1222285 - Part 1: Spoofing the keyboard event to mimc a certain keyboard layout according to the content-language of the document when 'privacy.resistFingerprinting' is true. r?masayuki,arthuredelstein,smaug This patch makes Firefox to spoof keyboardEvent.code, keyboardEvent.keycode and modifier states, for 'Shift', 'Alt', 'Control' and 'AltGraph', when 'privacy.resistFingerprinting' is true. Firefox will spoof keyboard events as a certain keyboard layout according to the content language of the document, for example, we use US English keyboard for English content. Right now, it only supports English contents, we will add more support for more languages later. The spoofing only affects content, chrome can still see real keyboard events. MozReview-Commit-ID: 40JPvwLmMMB
dom/events/KeyboardEvent.cpp
dom/events/KeyboardEvent.h
dom/webidl/KeyboardEvent.webidl
toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/KeyboardEvent.h"
 #include "mozilla/TextEvents.h"
+#include "nsContentUtils.h"
 #include "prtime.h"
 
 namespace mozilla {
 namespace dom {
 
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
@@ -33,47 +34,70 @@ KeyboardEvent::KeyboardEvent(EventTarget
 NS_IMPL_ADDREF_INHERITED(KeyboardEvent, UIEvent)
 NS_IMPL_RELEASE_INHERITED(KeyboardEvent, UIEvent)
 
 NS_INTERFACE_MAP_BEGIN(KeyboardEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMKeyEvent)
 NS_INTERFACE_MAP_END_INHERITING(UIEvent)
 
 bool
-KeyboardEvent::AltKey()
+KeyboardEvent::AltKey(CallerType aCallerType)
 {
-  return mEvent->AsKeyboardEvent()->IsAlt();
+  bool altState = mEvent->AsKeyboardEvent()->IsAlt();
+
+  if (!ShouldResistFingerprinting(aCallerType)) {
+    return altState;
+  }
+
+  // We need to give a spoofed state for Alt key since it could be used as a
+  // modifier key in certain keyboard layout. For example, the '@' key for
+  // German keyboard for MAC is Alt+L.
+  return GetSpoofedModifierStates(Modifier::MODIFIER_ALT, altState);
 }
 
 NS_IMETHODIMP
 KeyboardEvent::GetAltKey(bool* aIsDown)
 {
   NS_ENSURE_ARG_POINTER(aIsDown);
   *aIsDown = AltKey();
   return NS_OK;
 }
 
 bool
-KeyboardEvent::CtrlKey()
+KeyboardEvent::CtrlKey(CallerType aCallerType)
 {
-  return mEvent->AsKeyboardEvent()->IsControl();
+  bool ctrlState = mEvent->AsKeyboardEvent()->IsControl();
+
+  if (!ShouldResistFingerprinting(aCallerType)) {
+    return ctrlState;
+  }
+
+  // We need to give a spoofed state for Control key since it could be used as a
+  // modifier key in certain asian keyboard layouts.
+  return GetSpoofedModifierStates(Modifier::MODIFIER_CONTROL, ctrlState);
 }
 
 NS_IMETHODIMP
 KeyboardEvent::GetCtrlKey(bool* aIsDown)
 {
   NS_ENSURE_ARG_POINTER(aIsDown);
   *aIsDown = CtrlKey();
   return NS_OK;
 }
 
 bool
-KeyboardEvent::ShiftKey()
+KeyboardEvent::ShiftKey(CallerType aCallerType)
 {
-  return mEvent->AsKeyboardEvent()->IsShift();
+  bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
+
+  if (!ShouldResistFingerprinting(aCallerType)) {
+    return shiftState;
+  }
+
+  return GetSpoofedModifierStates(Modifier::MODIFIER_SHIFT, shiftState);
 }
 
 NS_IMETHODIMP
 KeyboardEvent::GetShiftKey(bool* aIsDown)
 {
   NS_ENSURE_ARG_POINTER(aIsDown);
   *aIsDown = ShiftKey();
   return NS_OK;
@@ -126,19 +150,29 @@ KeyboardEvent::GetModifierState(const ns
 NS_IMETHODIMP
 KeyboardEvent::GetKey(nsAString& aKeyName)
 {
   mEvent->AsKeyboardEvent()->GetDOMKeyName(aKeyName);
   return NS_OK;
 }
 
 void
-KeyboardEvent::GetCode(nsAString& aCodeName)
+KeyboardEvent::GetCode(nsAString& aCodeName, CallerType aCallerType)
 {
-  mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
+  if (!ShouldResistFingerprinting(aCallerType)) {
+    mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
+    return;
+  }
+
+  // When fingerprinting resistance is enabled, we will give a spoofed code
+  // according to the content-language of the document.
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+
+  nsRFPService::GetSpoofedCode(doc, mEvent->AsKeyboardEvent(),
+                               aCodeName);
 }
 
 void KeyboardEvent::GetInitDict(KeyboardEventInit& aParam)
 {
   GetKey(aParam.mKey);
   GetCode(aParam.mCode);
   aParam.mLocation = Location();
   aParam.mRepeat = Repeat();
@@ -206,26 +240,46 @@ NS_IMETHODIMP
 KeyboardEvent::GetKeyCode(uint32_t* aKeyCode)
 {
   NS_ENSURE_ARG_POINTER(aKeyCode);
   *aKeyCode = KeyCode();
   return NS_OK;
 }
 
 uint32_t
-KeyboardEvent::KeyCode()
+KeyboardEvent::KeyCode(CallerType aCallerType)
 {
   // If this event is initialized with ctor, we shouldn't check event type.
   if (mInitializedByCtor) {
     return mEvent->AsKeyboardEvent()->mKeyCode;
   }
 
-  if (mEvent->HasKeyEventMessage()) {
+  if (!mEvent->HasKeyEventMessage()) {
+    return 0;
+  }
+
+  if (!ShouldResistFingerprinting(aCallerType)) {
     return mEvent->AsKeyboardEvent()->mKeyCode;
   }
+
+  // The keyCode should be zero if the char code is given.
+  if (CharCode()) {
+    return 0;
+  }
+
+  // When fingerprinting resistance is enabled, we will give a spoofed keyCode
+  // according to the content-language of the document.
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+  uint32_t spoofedKeyCode;
+
+  if (nsRFPService::GetSpoofedKeyCode(doc, mEvent->AsKeyboardEvent(),
+                                      spoofedKeyCode)) {
+    return spoofedKeyCode;
+  }
+
   return 0;
 }
 
 uint32_t
 KeyboardEvent::Which()
 {
   // If this event is initialized with ctor, which can have independent value.
   if (mInitializedByCtor) {
@@ -360,16 +414,75 @@ KeyboardEvent::InitKeyboardEvent(const n
 
   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
   keyEvent->mLocation = aLocation;
   keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   keyEvent->mKeyValue = aKey;
 }
 
+already_AddRefed<nsIDocument>
+KeyboardEvent::GetDocument()
+{
+  nsCOMPtr<nsIDocument> doc;
+  nsCOMPtr<EventTarget> eventTarget = InternalDOMEvent()->GetTarget();
+
+  if (eventTarget) {
+    nsCOMPtr<nsPIDOMWindowInner> win =
+      do_QueryInterface(eventTarget->GetOwnerGlobal());
+
+    if (win) {
+      doc = win->GetExtantDoc();
+    }
+  }
+
+  return doc.forget();
+}
+
+bool
+KeyboardEvent::ShouldResistFingerprinting(CallerType aCallerType)
+{
+  // There are five situations we don't need to spoof this keyboard event.
+  //   1. This event is generated by scripts.
+  //   2. This event is from Numpad.
+  //   3. This event is in the system group.
+  //   4. The caller type is system.
+  //   5. The pref privcy.resistFingerprinting' is false, we fast return here since
+  //      we don't need to do any QI of following codes.
+  if (mInitializedByCtor ||
+      aCallerType == CallerType::System ||
+      mEvent->mFlags.mInSystemGroup ||
+      !nsContentUtils::ShouldResistFingerprinting() ||
+      mEvent->AsKeyboardEvent()->mLocation ==
+        nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD) {
+    return false;
+  }
+
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+
+  return doc && !nsContentUtils::IsChromeDoc(doc);
+}
+
+bool
+KeyboardEvent::GetSpoofedModifierStates(const Modifiers aModifierKey,
+                                        const bool aRawModifierState)
+{
+  bool spoofedState;
+  nsCOMPtr<nsIDocument> doc = GetDocument();
+
+  if(nsRFPService::GetSpoofedModifierStates(doc,
+                                            mEvent->AsKeyboardEvent(),
+                                            aModifierKey,
+                                            spoofedState)) {
+    return spoofedState;
+  }
+
+  return aRawModifierState;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 already_AddRefed<KeyboardEvent>
 NS_NewDOMKeyboardEvent(EventTarget* aOwner,
--- a/dom/events/KeyboardEvent.h
+++ b/dom/events/KeyboardEvent.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_KeyboardEvent_h_
 #define mozilla_dom_KeyboardEvent_h_
 
 #include "mozilla/dom/UIEvent.h"
 #include "mozilla/dom/KeyboardEventBinding.h"
 #include "mozilla/EventForwards.h"
 #include "nsIDOMKeyEvent.h"
+#include "nsRFPService.h"
 
 namespace mozilla {
 namespace dom {
 
 class KeyboardEvent : public UIEvent,
                       public nsIDOMKeyEvent
 {
 public:
@@ -37,34 +38,42 @@ public:
                                            const KeyboardEventInit& aParam,
                                            ErrorResult& aRv);
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return KeyboardEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
-  bool AltKey();
-  bool CtrlKey();
-  bool ShiftKey();
+  bool AltKey(CallerType aCallerType = CallerType::System);
+  bool CtrlKey(CallerType aCallerType = CallerType::System);
+  bool ShiftKey(CallerType aCallerType = CallerType::System);
   bool MetaKey();
 
-  bool GetModifierState(const nsAString& aKey)
+  bool GetModifierState(const nsAString& aKey,
+                        CallerType aCallerType = CallerType::System)
   {
-    return GetModifierStateInternal(aKey);
+    bool modifierState = GetModifierStateInternal(aKey);
+
+    if (!ShouldResistFingerprinting(aCallerType)) {
+      return modifierState;
+    }
+
+    Modifiers modifier = WidgetInputEvent::GetModifier(aKey);
+    return GetSpoofedModifierStates(modifier, modifierState);
   }
 
   bool Repeat();
   bool IsComposing();
   uint32_t CharCode();
-  uint32_t KeyCode();
+  uint32_t KeyCode(CallerType aCallerType = CallerType::System);
   virtual uint32_t Which() override;
   uint32_t Location();
 
-  void GetCode(nsAString& aCode);
+  void GetCode(nsAString& aCode, CallerType aCallerType = CallerType::System);
   void GetInitDict(KeyboardEventInit& aParam);
 
   void InitKeyEvent(const nsAString& aType, bool aCanBubble, bool aCancelable,
                     nsGlobalWindowInner* aView, bool aCtrlKey, bool aAltKey,
                     bool aShiftKey, bool aMetaKey,
                     uint32_t aKeyCode, uint32_t aCharCode)
   {
     auto* view = aView ? aView->AsInner() : nullptr;
@@ -89,16 +98,31 @@ protected:
 private:
   // True, if the instance is created with Constructor().
   bool mInitializedByCtor;
 
   // If the instance is created with Constructor(), which may have independent
   // value.  mInitializedWhichValue stores it.  I.e., this is invalid when
   // mInitializedByCtor is false.
   uint32_t mInitializedWhichValue;
+
+  // This method returns the boolean to indicate whether spoofing keyboard
+  // event for fingerprinting resistance. It will return true when pref
+  // 'privacy.resistFingerprinting' is true and the event target is content.
+  // Otherwise, it will return false.
+  bool ShouldResistFingerprinting(CallerType aCallerType);
+
+  // This method returns the nsIDocument which is associated with the event
+  // target.
+  already_AddRefed<nsIDocument> GetDocument();
+
+  // This method returns the spoofed modifier state of the given modifier key
+  // for fingerprinting resistance.
+  bool GetSpoofedModifierStates(const Modifiers aModifierKey,
+                                const bool aRawModifierState);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 already_AddRefed<mozilla::dom::KeyboardEvent>
 NS_NewDOMKeyboardEvent(mozilla::dom::EventTarget* aOwner,
                        nsPresContext* aPresContext,
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -3,35 +3,41 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)]
 interface KeyboardEvent : UIEvent
 {
   readonly attribute unsigned long    charCode;
+  [NeedsCallerType]
   readonly attribute unsigned long    keyCode;
 
+  [NeedsCallerType]
   readonly attribute boolean          altKey;
+  [NeedsCallerType]
   readonly attribute boolean          ctrlKey;
+  [NeedsCallerType]
   readonly attribute boolean          shiftKey;
   readonly attribute boolean          metaKey;
 
+  [NeedsCallerType]
   boolean getModifierState(DOMString key);
 
   const unsigned long DOM_KEY_LOCATION_STANDARD = 0x00;
   const unsigned long DOM_KEY_LOCATION_LEFT     = 0x01;
   const unsigned long DOM_KEY_LOCATION_RIGHT    = 0x02;
   const unsigned long DOM_KEY_LOCATION_NUMPAD   = 0x03;
 
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
   readonly attribute boolean       isComposing;
 
   readonly attribute DOMString key;
+  [NeedsCallerType]
   readonly attribute DOMString code;
 
   [Throws]
   void initKeyboardEvent(DOMString typeArg,
                          optional boolean bubblesArg = false,
                          optional boolean cancelableArg = false,
                          optional Window? viewArg = null,
                          optional DOMString keyArg = "",
new file mode 100644
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/KeyCodeConsensus_En_US.h
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file contains the spoofed keycodes of en-US for fingerprinting resistance.
+ * When privacy.resistFingerprinting is active, we spoof the user's keyboard
+ * layout according to the language of the document.
+ *
+ * Use CONTROL to define the control key.
+ *   CONTROL(keyNameIndex, codeNameIndex, keyCode)
+ *   @param keyNameIndex  The keyNameIndex of this control key.
+ *                        See KeyNameList.h for details.
+ *   @param codeNameIndex The codeNameIndex of this contorl key.
+ *                        See PhysicalKeyCodeNameList.h for details.
+ *   @param keyCode       The keyCode of this control key.
+ *                        See nsIDOMKeyEvent.idl for details.
+ *
+ * Use KEY to define the key with its modifier states. The key will be spoofed
+ * with given modifier states.
+ *   KEY(keyString, codeNameIndex, keyCode, modifiers)
+ *   @param keyString     The key string of this key.
+ *   @param codeNameIndex The codeNameIndex of this key.
+ *                        See PhysicalKeyCodeNameList.h for details.
+ *   @param keyCode       The keyCode of this key.
+ *                        See nsIDOMKeyEvent.idl for details.
+ *   @param modifiers     The spoofing modifier states for this key.
+ *                        See BasicEvents.h for details.
+ */
+
+/**
+ * Spoofed keycodes for English content (US English keyboard layout).
+ */
+
+CONTROL(Alt,         AltLeft,     nsIDOMKeyEvent::DOM_VK_ALT)
+CONTROL(ArrowDown,   ArrowDown,   nsIDOMKeyEvent::DOM_VK_DOWN)
+CONTROL(ArrowLeft,   ArrowLeft,   nsIDOMKeyEvent::DOM_VK_LEFT)
+CONTROL(ArrowRight,  ArrowRight,  nsIDOMKeyEvent::DOM_VK_RIGHT)
+CONTROL(ArrowUp,     ArrowUp,     nsIDOMKeyEvent::DOM_VK_UP)
+CONTROL(Backspace,   Backspace,   nsIDOMKeyEvent::DOM_VK_BACK_SPACE)
+CONTROL(CapsLock,    CapsLock,    nsIDOMKeyEvent::DOM_VK_CAPS_LOCK)
+// Leaving "ContextMenu" key unimplemented; not every english keyboard has this.
+// For example, MACOS doesn't have this.
+CONTROL(Control,     ControlLeft, nsIDOMKeyEvent::DOM_VK_CONTROL)
+CONTROL(Delete,      Delete,      nsIDOMKeyEvent::DOM_VK_DELETE)
+CONTROL(End,         End,         nsIDOMKeyEvent::DOM_VK_END)
+CONTROL(Enter,       Enter,       nsIDOMKeyEvent::DOM_VK_RETURN)
+CONTROL(Escape,      Escape,      nsIDOMKeyEvent::DOM_VK_ESCAPE)
+// Leaving "Help" key unimplemented; it only appears in some keyboard in Linux.
+CONTROL(Home,        Home,        nsIDOMKeyEvent::DOM_VK_HOME)
+CONTROL(Insert,      Insert,      nsIDOMKeyEvent::DOM_VK_INSERT)
+CONTROL(Meta,        OSLeft,      nsIDOMKeyEvent::DOM_VK_WIN)
+CONTROL(OS,          OSLeft,      nsIDOMKeyEvent::DOM_VK_WIN)
+CONTROL(PageDown,    PageDown,    nsIDOMKeyEvent::DOM_VK_PAGE_DOWN)
+CONTROL(PageUp,      PageUp,      nsIDOMKeyEvent::DOM_VK_PAGE_UP)
+// Leaving "Pause", "PrintScreen" and "ScrollLock" keys unimplemented; they are
+// non-MACOS only.
+CONTROL(Shift,       ShiftLeft,   nsIDOMKeyEvent::DOM_VK_SHIFT)
+CONTROL(Tab,         Tab,         nsIDOMKeyEvent::DOM_VK_TAB)
+CONTROL(F1,          F1,          nsIDOMKeyEvent::DOM_VK_F1)
+CONTROL(F2,          F2,          nsIDOMKeyEvent::DOM_VK_F2)
+CONTROL(F3,          F3,          nsIDOMKeyEvent::DOM_VK_F3)
+CONTROL(F4,          F4,          nsIDOMKeyEvent::DOM_VK_F4)
+CONTROL(F5,          F5,          nsIDOMKeyEvent::DOM_VK_F5)
+CONTROL(F6,          F6,          nsIDOMKeyEvent::DOM_VK_F6)
+CONTROL(F7,          F7,          nsIDOMKeyEvent::DOM_VK_F7)
+CONTROL(F8,          F8,          nsIDOMKeyEvent::DOM_VK_F8)
+CONTROL(F9,          F9,          nsIDOMKeyEvent::DOM_VK_F9)
+CONTROL(F10,         F10,         nsIDOMKeyEvent::DOM_VK_F10)
+CONTROL(F11,         F11,         nsIDOMKeyEvent::DOM_VK_F11)
+CONTROL(F12,         F12,         nsIDOMKeyEvent::DOM_VK_F12)
+// Leaving "F13" to "F35" key unimplemented; they are some how platform dependent.
+// "F13" to "F19" are on MAC's full keyboard but may not exist on usual keyboard.
+// "F20" to "F24" are only available on Windows and Linux.
+// "F25" to "F35" are Linux only.
+// Leaving "Clear" key unimplemented; it's inconsistent between platforms.
+KEY(" ",  Space,        nsIDOMKeyEvent::DOM_VK_SPACE,  MODIFIER_NONE)
+KEY(",",  Comma,        nsIDOMKeyEvent::DOM_VK_COMMA, MODIFIER_NONE)
+KEY("<",  Comma,        nsIDOMKeyEvent::DOM_VK_COMMA, MODIFIER_SHIFT)
+KEY(".",  Period,       nsIDOMKeyEvent::DOM_VK_PERIOD, MODIFIER_NONE)
+KEY(">",  Period,       nsIDOMKeyEvent::DOM_VK_PERIOD, MODIFIER_SHIFT)
+KEY("/",  Slash,        nsIDOMKeyEvent::DOM_VK_SLASH, MODIFIER_NONE)
+KEY("?",  Slash,        nsIDOMKeyEvent::DOM_VK_SLASH, MODIFIER_SHIFT)
+KEY(";",  Semicolon,    nsIDOMKeyEvent::DOM_VK_SEMICOLON,  MODIFIER_NONE)
+KEY(":",  Semicolon,    nsIDOMKeyEvent::DOM_VK_SEMICOLON,  MODIFIER_SHIFT)
+KEY("'",  Quote,        nsIDOMKeyEvent::DOM_VK_QUOTE, MODIFIER_NONE)
+KEY("\"", Quote,        nsIDOMKeyEvent::DOM_VK_QUOTE, MODIFIER_SHIFT)
+KEY("[",  BracketLeft,  nsIDOMKeyEvent::DOM_VK_OPEN_BRACKET, MODIFIER_NONE)
+KEY("{",  BracketLeft,  nsIDOMKeyEvent::DOM_VK_OPEN_BRACKET, MODIFIER_SHIFT)
+KEY("]",  BracketRight, nsIDOMKeyEvent::DOM_VK_CLOSE_BRACKET, MODIFIER_NONE)
+KEY("}",  BracketRight, nsIDOMKeyEvent::DOM_VK_CLOSE_BRACKET, MODIFIER_SHIFT)
+KEY("`",  Backquote,    nsIDOMKeyEvent::DOM_VK_BACK_QUOTE, MODIFIER_NONE)
+KEY("~",  Backquote,    nsIDOMKeyEvent::DOM_VK_BACK_QUOTE, MODIFIER_SHIFT)
+KEY("\\", Backslash,    nsIDOMKeyEvent::DOM_VK_BACK_SLASH, MODIFIER_NONE)
+KEY("|",  Backslash,    nsIDOMKeyEvent::DOM_VK_BACK_SLASH, MODIFIER_SHIFT)
+KEY("-",  Minus,        nsIDOMKeyEvent::DOM_VK_HYPHEN_MINUS, MODIFIER_NONE)
+KEY("_",  Minus,        nsIDOMKeyEvent::DOM_VK_HYPHEN_MINUS, MODIFIER_SHIFT)
+KEY("=",  Equal,        nsIDOMKeyEvent::DOM_VK_EQUALS, MODIFIER_NONE)
+KEY("+",  Equal,        nsIDOMKeyEvent::DOM_VK_EQUALS, MODIFIER_SHIFT)
+KEY("A",  KeyA,         nsIDOMKeyEvent::DOM_VK_A, MODIFIER_SHIFT)
+KEY("B",  KeyB,         nsIDOMKeyEvent::DOM_VK_B, MODIFIER_SHIFT)
+KEY("C",  KeyC,         nsIDOMKeyEvent::DOM_VK_C, MODIFIER_SHIFT)
+KEY("D",  KeyD,         nsIDOMKeyEvent::DOM_VK_D, MODIFIER_SHIFT)
+KEY("E",  KeyE,         nsIDOMKeyEvent::DOM_VK_E, MODIFIER_SHIFT)
+KEY("F",  KeyF,         nsIDOMKeyEvent::DOM_VK_F, MODIFIER_SHIFT)
+KEY("G",  KeyG,         nsIDOMKeyEvent::DOM_VK_G, MODIFIER_SHIFT)
+KEY("H",  KeyH,         nsIDOMKeyEvent::DOM_VK_H, MODIFIER_SHIFT)
+KEY("I",  KeyI,         nsIDOMKeyEvent::DOM_VK_I, MODIFIER_SHIFT)
+KEY("J",  KeyJ,         nsIDOMKeyEvent::DOM_VK_J, MODIFIER_SHIFT)
+KEY("K",  KeyK,         nsIDOMKeyEvent::DOM_VK_K, MODIFIER_SHIFT)
+KEY("L",  KeyL,         nsIDOMKeyEvent::DOM_VK_L, MODIFIER_SHIFT)
+KEY("M",  KeyM,         nsIDOMKeyEvent::DOM_VK_M, MODIFIER_SHIFT)
+KEY("N",  KeyN,         nsIDOMKeyEvent::DOM_VK_N, MODIFIER_SHIFT)
+KEY("O",  KeyO,         nsIDOMKeyEvent::DOM_VK_O, MODIFIER_SHIFT)
+KEY("P",  KeyP,         nsIDOMKeyEvent::DOM_VK_P, MODIFIER_SHIFT)
+KEY("Q",  KeyQ,         nsIDOMKeyEvent::DOM_VK_Q, MODIFIER_SHIFT)
+KEY("R",  KeyR,         nsIDOMKeyEvent::DOM_VK_R, MODIFIER_SHIFT)
+KEY("S",  KeyS,         nsIDOMKeyEvent::DOM_VK_S, MODIFIER_SHIFT)
+KEY("T",  KeyT,         nsIDOMKeyEvent::DOM_VK_T, MODIFIER_SHIFT)
+KEY("U",  KeyU,         nsIDOMKeyEvent::DOM_VK_U, MODIFIER_SHIFT)
+KEY("V",  KeyV,         nsIDOMKeyEvent::DOM_VK_V, MODIFIER_SHIFT)
+KEY("W",  KeyW,         nsIDOMKeyEvent::DOM_VK_W, MODIFIER_SHIFT)
+KEY("X",  KeyX,         nsIDOMKeyEvent::DOM_VK_X, MODIFIER_SHIFT)
+KEY("Y",  KeyY,         nsIDOMKeyEvent::DOM_VK_Y, MODIFIER_SHIFT)
+KEY("Z",  KeyZ,         nsIDOMKeyEvent::DOM_VK_Z, MODIFIER_SHIFT)
+KEY("a",  KeyA,         nsIDOMKeyEvent::DOM_VK_A, MODIFIER_NONE)
+KEY("b",  KeyB,         nsIDOMKeyEvent::DOM_VK_B, MODIFIER_NONE)
+KEY("c",  KeyC,         nsIDOMKeyEvent::DOM_VK_C, MODIFIER_NONE)
+KEY("d",  KeyD,         nsIDOMKeyEvent::DOM_VK_D, MODIFIER_NONE)
+KEY("e",  KeyE,         nsIDOMKeyEvent::DOM_VK_E, MODIFIER_NONE)
+KEY("f",  KeyF,         nsIDOMKeyEvent::DOM_VK_F, MODIFIER_NONE)
+KEY("g",  KeyG,         nsIDOMKeyEvent::DOM_VK_G, MODIFIER_NONE)
+KEY("h",  KeyH,         nsIDOMKeyEvent::DOM_VK_H, MODIFIER_NONE)
+KEY("i",  KeyI,         nsIDOMKeyEvent::DOM_VK_I, MODIFIER_NONE)
+KEY("j",  KeyJ,         nsIDOMKeyEvent::DOM_VK_J, MODIFIER_NONE)
+KEY("k",  KeyK,         nsIDOMKeyEvent::DOM_VK_K, MODIFIER_NONE)
+KEY("l",  KeyL,         nsIDOMKeyEvent::DOM_VK_L, MODIFIER_NONE)
+KEY("m",  KeyM,         nsIDOMKeyEvent::DOM_VK_M, MODIFIER_NONE)
+KEY("n",  KeyN,         nsIDOMKeyEvent::DOM_VK_N, MODIFIER_NONE)
+KEY("o",  KeyO,         nsIDOMKeyEvent::DOM_VK_O, MODIFIER_NONE)
+KEY("p",  KeyP,         nsIDOMKeyEvent::DOM_VK_P, MODIFIER_NONE)
+KEY("q",  KeyQ,         nsIDOMKeyEvent::DOM_VK_Q, MODIFIER_NONE)
+KEY("r",  KeyR,         nsIDOMKeyEvent::DOM_VK_R, MODIFIER_NONE)
+KEY("s",  KeyS,         nsIDOMKeyEvent::DOM_VK_S, MODIFIER_NONE)
+KEY("t",  KeyT,         nsIDOMKeyEvent::DOM_VK_T, MODIFIER_NONE)
+KEY("u",  KeyU,         nsIDOMKeyEvent::DOM_VK_U, MODIFIER_NONE)
+KEY("v",  KeyV,         nsIDOMKeyEvent::DOM_VK_V, MODIFIER_NONE)
+KEY("w",  KeyW,         nsIDOMKeyEvent::DOM_VK_W, MODIFIER_NONE)
+KEY("x",  KeyX,         nsIDOMKeyEvent::DOM_VK_X, MODIFIER_NONE)
+KEY("y",  KeyY,         nsIDOMKeyEvent::DOM_VK_Y, MODIFIER_NONE)
+KEY("z",  KeyZ,         nsIDOMKeyEvent::DOM_VK_Z, MODIFIER_NONE)
+KEY("0",  Digit0,       nsIDOMKeyEvent::DOM_VK_0, MODIFIER_NONE)
+KEY("1",  Digit1,       nsIDOMKeyEvent::DOM_VK_1, MODIFIER_NONE)
+KEY("2",  Digit2,       nsIDOMKeyEvent::DOM_VK_2, MODIFIER_NONE)
+KEY("3",  Digit3,       nsIDOMKeyEvent::DOM_VK_3, MODIFIER_NONE)
+KEY("4",  Digit4,       nsIDOMKeyEvent::DOM_VK_4, MODIFIER_NONE)
+KEY("5",  Digit5,       nsIDOMKeyEvent::DOM_VK_5, MODIFIER_NONE)
+KEY("6",  Digit6,       nsIDOMKeyEvent::DOM_VK_6, MODIFIER_NONE)
+KEY("7",  Digit7,       nsIDOMKeyEvent::DOM_VK_7, MODIFIER_NONE)
+KEY("8",  Digit8,       nsIDOMKeyEvent::DOM_VK_8, MODIFIER_NONE)
+KEY("9",  Digit9,       nsIDOMKeyEvent::DOM_VK_9, MODIFIER_NONE)
+KEY(")",  Digit0,       nsIDOMKeyEvent::DOM_VK_0, MODIFIER_SHIFT)
+KEY("!",  Digit1,       nsIDOMKeyEvent::DOM_VK_1, MODIFIER_SHIFT)
+KEY("@",  Digit2,       nsIDOMKeyEvent::DOM_VK_2, MODIFIER_SHIFT)
+KEY("#",  Digit3,       nsIDOMKeyEvent::DOM_VK_3, MODIFIER_SHIFT)
+KEY("$",  Digit4,       nsIDOMKeyEvent::DOM_VK_4, MODIFIER_SHIFT)
+KEY("%",  Digit5,       nsIDOMKeyEvent::DOM_VK_5, MODIFIER_SHIFT)
+KEY("^",  Digit6,       nsIDOMKeyEvent::DOM_VK_6, MODIFIER_SHIFT)
+KEY("&",  Digit7,       nsIDOMKeyEvent::DOM_VK_7, MODIFIER_SHIFT)
+KEY("*",  Digit8,       nsIDOMKeyEvent::DOM_VK_8, MODIFIER_SHIFT)
+KEY("(",  Digit9,       nsIDOMKeyEvent::DOM_VK_9, MODIFIER_SHIFT)
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -7,16 +7,17 @@
 
 #include <algorithm>
 #include <time.h>
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/TextEvents.h"
 
 #include "nsCOMPtr.h"
 #include "nsCoord.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsXULAppAPI.h"
 #include "nsPrintfCString.h"
 
@@ -41,26 +42,31 @@ using namespace std;
 #define RFP_SPOOFED_FRAMES_PER_SEC_PREF "privacy.resistFingerprinting.video_frames_per_sec"
 #define RFP_SPOOFED_DROPPED_RATIO_PREF  "privacy.resistFingerprinting.video_dropped_ratio"
 #define RFP_TARGET_VIDEO_RES_PREF "privacy.resistFingerprinting.target_video_res"
 #define RFP_SPOOFED_FRAMES_PER_SEC_DEFAULT 30
 #define RFP_SPOOFED_DROPPED_RATIO_DEFAULT  5
 #define RFP_TARGET_VIDEO_RES_DEFAULT 480
 #define PROFILE_INITIALIZED_TOPIC "profile-initial-state"
 
+#define RFP_DEFAULT_SPOOFING_KEYBOARD_LANG KeyboardLang::EN
+#define RFP_DEFAULT_SPOOFING_KEYBOARD_REGION KeyboardRegion::US
+
 NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
 
 static StaticRefPtr<nsRFPService> sRFPService;
 static bool sInitialized = false;
 Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyResistFingerprinting;
 Atomic<bool, ReleaseAcquire> nsRFPService::sPrivacyTimerPrecisionReduction;
 Atomic<uint32_t, ReleaseAcquire> sResolutionUSec;
 static uint32_t sVideoFramesPerSec;
 static uint32_t sVideoDroppedRatio;
 static uint32_t sTargetVideoRes;
+nsDataHashtable<KeyboardHashKey, const SpoofingKeyboardCode*>*
+  nsRFPService::sSpoofingKeyboardCodes = nullptr;
 
 /* static */
 nsRFPService*
 nsRFPService::GetOrCreate()
 {
   if (!sInitialized) {
     sRFPService = new nsRFPService();
     nsresult rv = sRFPService->Init();
@@ -369,16 +375,240 @@ nsRFPService::StartShutdown()
     if (prefs) {
       prefs->RemoveObserver(RESIST_FINGERPRINTING_PREF, this);
       prefs->RemoveObserver(RFP_TIMER_PREF, this);
       prefs->RemoveObserver(RFP_TIMER_VALUE_PREF, this);
     }
   }
 }
 
+/* static */
+void
+nsRFPService::MaybeCreateSpoofingKeyCodes(const KeyboardLangs aLang,
+                                          const KeyboardRegions aRegion)
+{
+  if (!sSpoofingKeyboardCodes) {
+    sSpoofingKeyboardCodes =
+      new nsDataHashtable<KeyboardHashKey, const SpoofingKeyboardCode*>();
+  }
+
+  if (KeyboardLang::EN == aLang) {
+    switch (aRegion) {
+      case KeyboardRegion::US:
+        MaybeCreateSpoofingKeyCodesForEnUS();
+        break;
+    }
+  }
+}
+
+/* static */
+void
+nsRFPService::MaybeCreateSpoofingKeyCodesForEnUS()
+{
+  MOZ_ASSERT(sSpoofingKeyboardCodes);
+
+  static bool sInitialized = false;
+  const KeyboardLangs lang = KeyboardLang::EN;
+  const KeyboardRegions reg = KeyboardRegion::US;
+
+  if (sInitialized) {
+    return;
+  }
+
+  static const SpoofingKeyboardInfo spoofingKeyboardInfoTable[] = {
+#define KEY(key_, _codeNameIdx, _keyCode, _modifier) \
+    { KEY_NAME_INDEX_USE_STRING, NS_LITERAL_STRING(key_), \
+      { CODE_NAME_INDEX_##_codeNameIdx, _keyCode, _modifier } },
+#define CONTROL(keyNameIdx_, _codeNameIdx, _keyCode) \
+    { KEY_NAME_INDEX_##keyNameIdx_, EmptyString(), \
+      { CODE_NAME_INDEX_##_codeNameIdx, _keyCode, MODIFIER_NONE } },
+#include "KeyCodeConsensus_En_US.h"
+#undef CONTROL
+#undef KEY
+  };
+
+  for (const auto& keyboardInfo : spoofingKeyboardInfoTable) {
+    KeyboardHashKey key(lang, reg,
+                        keyboardInfo.mKeyIdx,
+                        keyboardInfo.mKey);
+    MOZ_ASSERT(!sSpoofingKeyboardCodes->Lookup(key),
+               "Double-defining key code; fix your KeyCodeConsensus file");
+    sSpoofingKeyboardCodes->Put(key, &keyboardInfo.mSpoofingCode);
+  }
+
+  sInitialized = true;
+}
+
+/* static */
+void
+nsRFPService::GetKeyboardLangAndRegion(const nsAString& aLanguage,
+                                       KeyboardLangs& aLocale,
+                                       KeyboardRegions& aRegion)
+{
+  nsAutoString langStr;
+  nsAutoString regionStr;
+  uint32_t partNum = 0;
+
+  for (const nsAString& part : aLanguage.Split('-')) {
+    if (partNum == 0) {
+      langStr = part;
+    } else {
+      regionStr = part;
+      break;
+    }
+
+    partNum++;
+  }
+
+  // We test each language here as well as the region. There are some cases that
+  // only the language is given, we will use the default region code when this
+  // happens. The default region should depend on the given language.
+  if (langStr.EqualsLiteral(RFP_KEYBOARD_LANG_STRING_EN)) {
+    aLocale = KeyboardLang::EN;
+    // Give default values first.
+    aRegion = KeyboardRegion::US;
+
+    if (regionStr.EqualsLiteral(RFP_KEYBOARD_REGION_STRING_US)) {
+      aRegion = KeyboardRegion::US;
+    }
+  } else {
+    // There is no spoofed keyboard locale for the given language. We use the
+    // default one in this case.
+    aLocale = RFP_DEFAULT_SPOOFING_KEYBOARD_LANG;
+    aRegion = RFP_DEFAULT_SPOOFING_KEYBOARD_REGION;
+  }
+}
+
+/* static */
+bool
+nsRFPService::GetSpoofedKeyCodeInfo(const nsIDocument* aDoc,
+                                    const WidgetKeyboardEvent* aKeyboardEvent,
+                                    SpoofingKeyboardCode& aOut)
+{
+  MOZ_ASSERT(aKeyboardEvent);
+
+  KeyboardLangs keyboardLang = RFP_DEFAULT_SPOOFING_KEYBOARD_LANG;
+  KeyboardRegions keyboardRegion = RFP_DEFAULT_SPOOFING_KEYBOARD_REGION;
+  // If the document is given, we use the content language which is get from the
+  // document. Otherwise, we use the default one.
+  if (aDoc) {
+    nsAutoString language;
+    aDoc->GetContentLanguage(language);
+
+    // If the content-langauge is not given, we try to get langauge from the HTML
+    // lang attribute.
+    if (language.IsEmpty()) {
+      Element* elm = aDoc->GetHtmlElement();
+
+      if (elm) {
+        elm->GetLang(language);
+      }
+    }
+
+    // If two or more languages are given, per HTML5 spec, we should consider
+    // it as 'unknown'. So we use the default one.
+    if (!language.IsEmpty() &&
+        !language.Contains(char16_t(','))) {
+      language.StripWhitespace();
+      GetKeyboardLangAndRegion(language, keyboardLang,
+                               keyboardRegion);
+    }
+  }
+
+  MaybeCreateSpoofingKeyCodes(keyboardLang, keyboardRegion);
+
+  KeyNameIndex keyIdx = aKeyboardEvent->mKeyNameIndex;
+  nsAutoString keyName;
+
+  if (keyIdx == KEY_NAME_INDEX_USE_STRING) {
+    keyName = aKeyboardEvent->mKeyValue;
+  }
+
+  KeyboardHashKey key(keyboardLang, keyboardRegion, keyIdx, keyName);
+  const SpoofingKeyboardCode* keyboardCode = sSpoofingKeyboardCodes->Get(key);
+
+  if (keyboardCode) {
+    aOut = *keyboardCode;
+    return true;
+  }
+
+  return false;
+}
+
+/* static */
+bool
+nsRFPService::GetSpoofedModifierStates(const nsIDocument* aDoc,
+                                       const WidgetKeyboardEvent* aKeyboardEvent,
+                                       const Modifiers aModifier,
+                                       bool& aOut)
+{
+  MOZ_ASSERT(aKeyboardEvent);
+
+  // For modifier or control keys, we don't need to hide its modifier states.
+  if (aKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
+    return false;
+  }
+
+  // We will spoof the modifer state for Alt, Shift, AltGraph and Control.
+  if (aModifier & (MODIFIER_ALT | MODIFIER_SHIFT | MODIFIER_ALTGRAPH | MODIFIER_CONTROL)) {
+    SpoofingKeyboardCode keyCodeInfo;
+
+    if (GetSpoofedKeyCodeInfo(aDoc, aKeyboardEvent, keyCodeInfo)) {
+      aOut = keyCodeInfo.mModifierStates & aModifier;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/* static */
+bool
+nsRFPService::GetSpoofedCode(const nsIDocument* aDoc,
+                             const WidgetKeyboardEvent* aKeyboardEvent,
+                             nsAString& aOut)
+{
+  MOZ_ASSERT(aKeyboardEvent);
+
+  SpoofingKeyboardCode keyCodeInfo;
+
+  if (!GetSpoofedKeyCodeInfo(aDoc, aKeyboardEvent, keyCodeInfo)) {
+    return false;
+  }
+
+  WidgetKeyboardEvent::GetDOMCodeName(keyCodeInfo.mCode, aOut);
+
+  // We need to change the 'Left' with 'Right' if the location indicates
+  // it's a right key.
+  if (aKeyboardEvent->mLocation == nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT &&
+      StringEndsWith(aOut, NS_LITERAL_STRING("Left"))) {
+    aOut.ReplaceLiteral(aOut.Length() - 4, 4, u"Right");
+  }
+
+  return true;
+}
+
+/* static */
+bool
+nsRFPService::GetSpoofedKeyCode(const nsIDocument* aDoc,
+                                const WidgetKeyboardEvent* aKeyboardEvent,
+                                uint32_t& aOut)
+{
+  MOZ_ASSERT(aKeyboardEvent);
+
+  SpoofingKeyboardCode keyCodeInfo;
+
+  if (GetSpoofedKeyCodeInfo(aDoc, aKeyboardEvent, keyCodeInfo)) {
+    aOut = keyCodeInfo.mKeyCode;
+    return true;
+  }
+
+  return false;
+}
+
 NS_IMETHODIMP
 nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
                       const char16_t* aMessage)
 {
   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
     NS_ConvertUTF16toUTF8 pref(aMessage);
 
     if (pref.EqualsLiteral(RFP_TIMER_PREF) || pref.EqualsLiteral(RFP_TIMER_VALUE_PREF)) {
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -2,18 +2,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __nsRFPService_h__
 #define __nsRFPService_h__
 
 #include "mozilla/Atomics.h"
+#include "mozilla/EventForwards.h"
+#include "nsIDocument.h"
 #include "nsIObserver.h"
 
+#include "nsDataHashtable.h"
 #include "nsString.h"
 
 // Defines regarding spoofed values of Navigator object. These spoofed values
 // are returned when 'privacy.resistFingerprinting' is true.
 // We decided to give different spoofed values according to the platform. The
 // reason is that it is easy to detect the real platform. So there is no benefit
 // for hiding the platform: it only brings breakages, like keyboard shortcuts won't
 // work in MAC OS if we spoof it as a window platform.
@@ -41,16 +44,115 @@
 #define SPOOFED_PLATFORM   "Linux x86_64"
 #endif
 
 #define SPOOFED_APPNAME    "Netscape"
 #define LEGACY_BUILD_ID    "20100101"
 
 namespace mozilla {
 
+enum KeyboardLang {
+  EN = 0x01
+};
+
+#define RFP_KEYBOARD_LANG_STRING_EN "en"
+
+typedef uint8_t KeyboardLangs;
+
+enum KeyboardRegion {
+  US = 0x01
+};
+
+#define RFP_KEYBOARD_REGION_STRING_US "US"
+
+typedef uint8_t KeyboardRegions;
+
+// This struct has the information about how to spoof the keyboardEvent.code,
+// keyboardEvent.keycode and modifier states.
+struct SpoofingKeyboardCode
+{
+  CodeNameIndex mCode;
+  uint8_t mKeyCode;
+  Modifiers mModifierStates;
+};
+
+struct SpoofingKeyboardInfo
+{
+  KeyNameIndex mKeyIdx;
+  nsString mKey;
+  SpoofingKeyboardCode mSpoofingCode;
+};
+
+class KeyboardHashKey : public PLDHashEntryHdr
+{
+public:
+  typedef const KeyboardHashKey& KeyType;
+  typedef const KeyboardHashKey* KeyTypePointer;
+
+  KeyboardHashKey(const KeyboardLangs aLang,
+                  const KeyboardRegions aRegion,
+                  const KeyNameIndexType aKeyIdx,
+                  const nsAString &aKey)
+    : mLang(aLang)
+    , mRegion(aRegion)
+    , mKeyIdx(aKeyIdx)
+    , mKey(aKey)
+  {}
+
+  explicit KeyboardHashKey(KeyTypePointer aOther)
+    : mLang(aOther->mLang)
+    , mRegion(aOther->mRegion)
+    , mKeyIdx(aOther->mKeyIdx)
+    , mKey(aOther->mKey)
+  {}
+
+  KeyboardHashKey(KeyType aOther)
+    : mLang(aOther.mLang)
+    , mRegion(aOther.mRegion)
+    , mKeyIdx(aOther.mKeyIdx)
+    , mKey(aOther.mKey)
+  {}
+
+  ~KeyboardHashKey()
+  {}
+
+  bool KeyEquals(KeyTypePointer aOther) const
+  {
+    return mLang == aOther->mLang &&
+           mRegion == aOther->mRegion &&
+           mKeyIdx == aOther->mKeyIdx &&
+           mKey == aOther->mKey;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType aKey)
+  {
+    return &aKey;
+  }
+
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    nsAutoString temp;
+    temp.AppendInt(aKey->mLang);
+    temp.Append('|');
+    temp.AppendInt(aKey->mRegion);
+    temp.Append('|');
+    temp.AppendInt(aKey->mKeyIdx);
+    temp.Append('|');
+    temp.Append(aKey->mKey);
+    return mozilla::HashString(temp);
+  }
+
+  enum { ALLOW_MEMMOVE = true };
+
+  KeyboardLangs mLang;
+  KeyboardRegions mRegion;
+  KeyNameIndexType mKeyIdx;
+  nsString mKey;
+};
+
 class nsRFPService final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   static nsRFPService* GetOrCreate();
   static bool IsResistFingerprintingEnabled();
@@ -69,28 +171,79 @@ public:
   // depend on the video resolution.
   static uint32_t GetSpoofedTotalFrames(double aTime);
   static uint32_t GetSpoofedDroppedFrames(double aTime, uint32_t aWidth, uint32_t aHeight);
   static uint32_t GetSpoofedPresentedFrames(double aTime, uint32_t aWidth, uint32_t aHeight);
 
   // This method generates the spoofed value of User Agent.
   static nsresult GetSpoofedUserAgent(nsACString &userAgent);
 
+  /**
+   * This method for getting spoofed modifier states for the given keyboard event.
+   *
+   * @param aDoc           [in]  the owner's document for getting content language.
+   * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
+   * @param aModifier      [in]  the modifier that needs to be spoofed.
+   * @param aOut           [out] the spoofed state for the given modifier.
+   * @return               true if there is a spoofed state for the modifier.
+   */
+  static bool GetSpoofedModifierStates(const nsIDocument* aDoc,
+                                       const WidgetKeyboardEvent* aKeyboardEvent,
+                                       const Modifiers aModifier,
+                                       bool& aOut);
+
+  /**
+   * This method for getting spoofed code for the given keyboard event.
+   *
+   * @param aDoc           [in]  the owner's document for getting content language.
+   * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
+   * @param aOut           [out] the spoofed code.
+   * @return               true if there is a spoofed code in the fake keyboard layout.
+   */
+  static bool GetSpoofedCode(const nsIDocument* aDoc,
+                             const WidgetKeyboardEvent* aKeyboardEvent,
+                             nsAString& aOut);
+
+  /**
+   * This method for getting spoofed keyCode for the given keyboard event.
+   *
+   * @param aDoc           [in]  the owner's document for getting content language.
+   * @param aKeyboardEvent [in]  the keyboard event that needs to be spoofed.
+   * @param aOut           [out] the spoofed keyCode.
+   * @return               true if there is a spoofed keyCode in the fake keyboard layout.
+   */
+  static bool GetSpoofedKeyCode(const nsIDocument* aDoc,
+                                const WidgetKeyboardEvent* aKeyboardEvent,
+                                uint32_t& aOut);
+
 private:
   nsresult Init();
 
   nsRFPService() {}
 
   ~nsRFPService() {}
 
   void UpdateTimers();
   void UpdateRFPPref();
   void StartShutdown();
 
+  static void MaybeCreateSpoofingKeyCodes(const KeyboardLangs aLang,
+                                          const KeyboardRegions aRegion);
+  static void MaybeCreateSpoofingKeyCodesForEnUS();
+
+  static void GetKeyboardLangAndRegion(const nsAString& aLanguage,
+                                       KeyboardLangs& aLang,
+                                       KeyboardRegions& aRegion);
+  static bool GetSpoofedKeyCodeInfo(const nsIDocument* aDoc,
+                                    const WidgetKeyboardEvent* aKeyboardEvent,
+                                    SpoofingKeyboardCode& aOut);
+
   static Atomic<bool, ReleaseAcquire> sPrivacyResistFingerprinting;
   static Atomic<bool, ReleaseAcquire> sPrivacyTimerPrecisionReduction;
 
+  static nsDataHashtable<KeyboardHashKey, const SpoofingKeyboardCode*>* sSpoofingKeyboardCodes;
+
   nsCString mInitialTZValue;
 };
 
 } // mozilla namespace
 
 #endif /* __nsRFPService_h__ */