Merge inbound to mozilla-central. a=merge
authorshindli <shindli@mozilla.com>
Tue, 09 Oct 2018 12:27:34 +0300
changeset 440136 a9616aaeff87448588d57c295e16eb4caec420fb
parent 440123 a3a767ad55fbf01762d41f7e8bff91188e736daf (diff)
parent 440135 f25bc716b4cd5a813536b7d469661f33aa004310 (current diff)
child 440146 a2fc465a0dd88db35a108be38898c7637d935efe
child 440159 6e54a90860e7f94d2f84543bc5cc3812dfc12eee
push id34808
push usershindli@mozilla.com
push dateTue, 09 Oct 2018 09:27:59 +0000
treeherdermozilla-central@a9616aaeff87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
a9616aaeff87 / 64.0a1 / 20181009100040 / files
nightly linux64
a9616aaeff87 / 64.0a1 / 20181009100040 / files
nightly mac
a9616aaeff87 / 64.0a1 / 20181009100040 / files
nightly win32
a9616aaeff87 / 64.0a1 / 20181009100040 / files
nightly win64
a9616aaeff87 / 64.0a1 / 20181009100040 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/tests/mochitest/bugs/test_bug351601.html
--- a/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js
@@ -2,17 +2,18 @@
  * Bug 1222285 - A test case for testing whether keyboard events be spoofed correctly
  *   when fingerprinting resistance is enable.
  */
 
 const CC = Components.Constructor;
 
 const kStrictKeyPressEvents =
   SpecialPowers.getBoolPref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content");
-
+const kSameKeyCodeAndCharCodeValue =
+  SpecialPowers.getBoolPref("dom.keyboardevent.keypress.set_keycode_and_charcode_to_same_value");
 const SHOULD_DELIVER_KEYDOWN          = 0x1;
 const SHOULD_DELIVER_KEYPRESS         = 0x2;
 const SHOULD_DELIVER_KEYUP            = 0x4;
 const SHOULD_DELIVER_ALL_FOR_PRINTABLE = SHOULD_DELIVER_KEYDOWN |
                                          SHOULD_DELIVER_KEYPRESS |
                                          SHOULD_DELIVER_KEYUP;
 const SHOULD_DELIVER_ALL_FOR_NON_PRINTABLE =
   kStrictKeyPressEvents ? (SHOULD_DELIVER_KEYDOWN | SHOULD_DELIVER_KEYUP) : SHOULD_DELIVER_ALL_FOR_PRINTABLE;
@@ -647,47 +648,55 @@ async function testKeyEvent(aTab, aTestC
 
   if (aTestCase.expectedKeyEvent & SHOULD_DELIVER_KEYUP) {
     testEvents.push("keyup");
   }
 
   let allKeyEventPromises = [];
 
   for (let testEvent of testEvents) {
-    let keyEventPromise = ContentTask.spawn(aTab.linkedBrowser, {testEvent, result: aTestCase.result}, async (aInput) => {
-      function verifyKeyboardEvent(aEvent, aResult) {
+    let keyEventPromise = ContentTask.spawn(aTab.linkedBrowser, {testEvent, result: aTestCase.result, kSameKeyCodeAndCharCodeValue}, async (aInput) => {
+      function verifyKeyboardEvent(aEvent, aResult, aSameKeyCodeAndCharCodeValue) {
         is(aEvent.key, aResult.key, "KeyboardEvent.key is correctly spoofed.");
         is(aEvent.code, aResult.code, "KeyboardEvent.code is correctly spoofed.");
         is(aEvent.location, aResult.location, "KeyboardEvent.location is correctly spoofed.");
         is(aEvent.altKey, aResult.altKey, "KeyboardEvent.altKey is correctly spoofed.");
         is(aEvent.shiftKey, aResult.shiftKey, "KeyboardEvent.shiftKey is correctly spoofed.");
         is(aEvent.ctrlKey, aResult.ctrlKey, "KeyboardEvent.ctrlKey is correctly spoofed.");
 
         // If the charCode is not 0, this is a character. The keyCode will be remained as 0.
         // Otherwise, we should check the keyCode.
-        if (aEvent.charCode != 0) {
-          is(aEvent.keyCode, 0, "KeyboardEvent.keyCode should be 0 for this case.");
+        if (!aSameKeyCodeAndCharCodeValue) {
+          if (aEvent.charCode != 0) {
+            is(aEvent.keyCode, 0, "KeyboardEvent.keyCode should be 0 for this case.");
+            is(aEvent.charCode, aResult.charCode, "KeyboardEvent.charCode is correctly spoofed.");
+          } else {
+            is(aEvent.keyCode, aResult.keyCode, "KeyboardEvent.keyCode is correctly spoofed.");
+            is(aEvent.charCode, 0, "KeyboardEvent.charCode should be 0 for this case.");
+          }
+        } else if (aResult.charCode) {
+          is(aEvent.keyCode, aResult.charCode, "KeyboardEvent.keyCode should be same as expected charCode for this case.");
           is(aEvent.charCode, aResult.charCode, "KeyboardEvent.charCode is correctly spoofed.");
         } else {
           is(aEvent.keyCode, aResult.keyCode, "KeyboardEvent.keyCode is correctly spoofed.");
-          is(aEvent.charCode, 0, "KeyboardEvent.charCode should be 0 for this case.");
+          is(aEvent.charCode, aResult.keyCode, "KeyboardEvent.charCode should be same as expected keyCode for this case.");
         }
 
         // Check getModifierState().
         is(aEvent.modifierState.Alt, aResult.altKey,
             "KeyboardEvent.getModifierState() reports a correctly spoofed value for 'Alt'.");
         is(aEvent.modifierState.AltGraph, aResult.altGraphKey,
             "KeyboardEvent.getModifierState() reports a correctly spoofed value for 'AltGraph'.");
         is(aEvent.modifierState.Shift, aResult.shiftKey,
             `KeyboardEvent.getModifierState() reports a correctly spoofed value for 'Shift'.`);
         is(aEvent.modifierState.Control, aResult.ctrlKey,
             `KeyboardEvent.getModifierState() reports a correctly spoofed value for 'Control'.`);
       }
 
-      let {testEvent: eventType, result} = aInput;
+      let {testEvent: eventType, result, kSameKeyCodeAndCharCodeValue: sameKeyCodeAndCharCodeValue} = aInput;
       let inputBox = content.document.getElementById("test");
 
       // We need to put the real access of event object into the content page instead of
       // here, ContentTask.spawn, since the script running here is under chrome privilege.
       // So the fingerprinting resistance won't work here.
       let resElement = content.document.getElementById("result-" + eventType);
 
       // First, try to focus on the input box.
@@ -703,17 +712,18 @@ async function testKeyEvent(aTab, aTestC
         }
       });
 
       // Once the result of the keyboard event ready, the content page will send
       // a custom event 'resultAvailable' for informing the script to check the
       // result.
       await new Promise(resolve => {
         function eventHandler(aEvent) {
-          verifyKeyboardEvent(JSON.parse(resElement.value), result);
+          verifyKeyboardEvent(JSON.parse(resElement.value), result,
+                              eventType == "keypress" && sameKeyCodeAndCharCodeValue);
           resElement.removeEventListener("resultAvailable", eventHandler, true);
           resolve();
         }
 
         resElement.addEventListener("resultAvailable", eventHandler, true);
       });
     });
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -489,16 +489,20 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::DOMIntersectionObserver',
 },
 
 'IntersectionObserverEntry': {
     'nativeType': 'mozilla::dom::DOMIntersectionObserverEntry',
     'headerFile': 'DOMIntersectionObserver.h',
 },
 
+'KeyboardEvent': {
+    'binaryNames': { 'constructor': 'ConstructorJS' },
+},
+
 'KeyEvent': {
     'concrete': False
 },
 
 'LegacyMozTCPSocket': {
     'headerFile': 'TCPSocket.h',
     'wrapperCache': False,
 },
--- a/dom/events/KeyboardEvent.cpp
+++ b/dom/events/KeyboardEvent.cpp
@@ -13,16 +13,17 @@ namespace mozilla {
 namespace dom {
 
 KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
                              nsPresContext* aPresContext,
                              WidgetKeyboardEvent* aEvent)
   : UIEvent(aOwner, aPresContext,
             aEvent ? aEvent :
                      new WidgetKeyboardEvent(false, eVoidEvent, nullptr))
+  , mInitializedByJS(false)
   , mInitializedByCtor(false)
   , mInitializedWhichValue(0)
 {
   if (aEvent) {
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
@@ -140,67 +141,125 @@ void KeyboardEvent::GetInitDict(Keyboard
   aParam.mModifierSymbol = internalEvent->IsSymbol();
   aParam.mModifierSymbolLock = internalEvent->IsSymbolLocked();
 
   // EventInit
   aParam.mBubbles =  internalEvent->mFlags.mBubbles;
   aParam.mCancelable = internalEvent->mFlags.mCancelable;
 }
 
-uint32_t
-KeyboardEvent::CharCode()
+bool
+KeyboardEvent::ShouldUseSameValueForCharCodeAndKeyCode(
+                 CallerType aCallerType) const
 {
-  // If this event is initialized with ctor, we shouldn't check event type.
-  if (mInitializedByCtor) {
-    return mEvent->AsKeyboardEvent()->mCharCode;
+  // - If this event is initialized by JS, we don't need to return same value
+  //   for keyCode and charCode since they can be initialized separately.
+  // - If this is not a keypress event, we shouldn't return same value for
+  //   keyCode and charCode.
+  // - If this event is referred by default handler, i.e., the caller is
+  //   system or this event is now in the system group, we don't need to use
+  //   hack for web-compat.
+  if (mInitializedByJS ||
+      mEvent->mMessage != eKeyPress ||
+      aCallerType == CallerType::System ||
+      mEvent->mFlags.mInSystemGroup) {
+    return false;
   }
 
-  switch (mEvent->mMessage) {
-  case eKeyDown:
-  case eKeyDownOnPlugin:
-  case eKeyUp:
-  case eKeyUpOnPlugin:
-    return 0;
-  case eKeyPress:
-  case eAccessKeyNotFound:
-    return mEvent->AsKeyboardEvent()->mCharCode;
-  default:
-    break;
+  MOZ_ASSERT(aCallerType == CallerType::NonSystem);
+
+  return StaticPrefs::
+    dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value();
+}
+
+uint32_t
+KeyboardEvent::CharCode(CallerType aCallerType)
+{
+  WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
+  if (mInitializedByJS) {
+    // If this is initialized by Ctor, we should return the initialized value.
+    if (mInitializedByCtor) {
+      return widgetKeyboardEvent->mCharCode;
+    }
+    // Otherwise, i.e., initialized by InitKey*Event(), we should return the
+    // initialized value only when eKeyPress or eAccessKeyNotFound event.
+    // Although this is odd, but our traditional behavior.
+    return widgetKeyboardEvent->mMessage == eKeyPress ||
+           widgetKeyboardEvent->mMessage == eAccessKeyNotFound ?
+             widgetKeyboardEvent->mCharCode : 0;
   }
-  return 0;
+
+  // If the key is a function key, we should return the result of KeyCode()
+  // even from CharCode().  Otherwise, i.e., the key may be a printable
+  // key or actually a printable key, we should return the given charCode
+  // value.
+
+  if (widgetKeyboardEvent->mKeyNameIndex != KEY_NAME_INDEX_USE_STRING &&
+      ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
+    return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
+  }
+
+  return widgetKeyboardEvent->mCharCode;
 }
 
 uint32_t
 KeyboardEvent::KeyCode(CallerType aCallerType)
 {
-  // If this event is initialized with ctor, we shouldn't check event type.
-  if (mInitializedByCtor) {
-    return mEvent->AsKeyboardEvent()->mKeyCode;
+  WidgetKeyboardEvent* widgetKeyboardEvent = mEvent->AsKeyboardEvent();
+  if (mInitializedByJS) {
+    // If this is initialized by Ctor, we should return the initialized value.
+    if (mInitializedByCtor) {
+      return widgetKeyboardEvent->mKeyCode;
+    }
+    // Otherwise, i.e., initialized by InitKey*Event(), we should return the
+    // initialized value only when the event message is a valid keyboard event
+    // message.  Although this is odd, but our traditional behavior.
+    // NOTE: The fix of bug 1222285 changed the behavior temporarily if
+    //       spoofing is enabled.  However, the behavior does not make sense
+    //       since if the event is generated by JS, the behavior shouldn't
+    //       be changed by whether spoofing is enabled or not.  Therefore,
+    //       we take back the original behavior.
+    return widgetKeyboardEvent->HasKeyEventMessage() ?
+             widgetKeyboardEvent->mKeyCode : 0;
   }
 
-  if (!mEvent->HasKeyEventMessage()) {
-    return 0;
+  // If the key is not a function key, i.e., the key may be a printable key
+  // or a function key mapped as a printable key, we should use charCode value
+  // for keyCode value if this is a "keypress" event.
+
+  if (widgetKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_USE_STRING &&
+      ShouldUseSameValueForCharCodeAndKeyCode(aCallerType)) {
+    return widgetKeyboardEvent->mCharCode;
   }
 
+  return ComputeTraditionalKeyCode(*widgetKeyboardEvent, aCallerType);
+}
+
+uint32_t
+KeyboardEvent::ComputeTraditionalKeyCode(WidgetKeyboardEvent& aKeyboardEvent,
+                                         CallerType aCallerType)
+{
   if (!ShouldResistFingerprinting(aCallerType)) {
-    return mEvent->AsKeyboardEvent()->mKeyCode;
+    return aKeyboardEvent.mKeyCode;
   }
 
-  // The keyCode should be zero if the char code is given.
-  if (CharCode()) {
+  // In Netscape style (i.e., traditional behavior of Gecko), the keyCode
+  // should be zero if the char code is given.
+  if ((mEvent->mMessage == eKeyPress ||
+       mEvent->mMessage == eAccessKeyNotFound) &&
+      aKeyboardEvent.mCharCode) {
     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)) {
+  if (nsRFPService::GetSpoofedKeyCode(doc, &aKeyboardEvent, spoofedKeyCode)) {
     return spoofedKeyCode;
   }
 
   return 0;
 }
 
 uint32_t
 KeyboardEvent::Which(CallerType aCallerType)
@@ -236,42 +295,43 @@ KeyboardEvent::Which(CallerType aCallerT
 uint32_t
 KeyboardEvent::Location()
 {
   return mEvent->AsKeyboardEvent()->mLocation;
 }
 
 // static
 already_AddRefed<KeyboardEvent>
-KeyboardEvent::Constructor(const GlobalObject& aGlobal,
-                           const nsAString& aType,
-                           const KeyboardEventInit& aParam,
-                           ErrorResult& aRv)
+KeyboardEvent::ConstructorJS(const GlobalObject& aGlobal,
+                             const nsAString& aType,
+                             const KeyboardEventInit& aParam,
+                             ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
   RefPtr<KeyboardEvent> newEvent =
     new KeyboardEvent(target, nullptr, nullptr);
   newEvent->InitWithKeyboardEventInit(target, aType, aParam, aRv);
 
   return newEvent.forget();
 }
 
 void
 KeyboardEvent::InitWithKeyboardEventInit(EventTarget* aOwner,
                                          const nsAString& aType,
                                          const KeyboardEventInit& aParam,
                                          ErrorResult& aRv)
 {
   bool trusted = Init(aOwner);
-  InitKeyEvent(aType, aParam.mBubbles, aParam.mCancelable,
-               aParam.mView, false, false, false, false,
-               aParam.mKeyCode, aParam.mCharCode);
+  InitKeyEventJS(aType, aParam.mBubbles, aParam.mCancelable,
+                 aParam.mView, false, false, false, false,
+                 aParam.mKeyCode, aParam.mCharCode);
   InitModifiers(aParam);
   SetTrusted(trusted);
   mDetail = aParam.mDetail;
+  mInitializedByJS = true;
   mInitializedByCtor = true;
   mInitializedWhichValue = aParam.mWhich;
 
   WidgetKeyboardEvent* internalEvent = mEvent->AsKeyboardEvent();
   internalEvent->mLocation = aParam.mLocation;
   internalEvent->mIsRepeat = aParam.mRepeat;
   internalEvent->mIsComposing = aParam.mIsComposing;
   internalEvent->mKeyNameIndex =
@@ -282,46 +342,50 @@ KeyboardEvent::InitWithKeyboardEventInit
   internalEvent->mCodeNameIndex =
     WidgetKeyboardEvent::GetCodeNameIndex(aParam.mCode);
   if (internalEvent->mCodeNameIndex == CODE_NAME_INDEX_USE_STRING) {
     internalEvent->mCodeValue = aParam.mCode;
   }
 }
 
 void
-KeyboardEvent::InitKeyEvent(const nsAString& aType, bool aCanBubble,
-                            bool aCancelable, nsGlobalWindowInner* aView,
-                            bool aCtrlKey, bool aAltKey,
-                            bool aShiftKey, bool aMetaKey,
-                            uint32_t aKeyCode, uint32_t aCharCode)
+KeyboardEvent::InitKeyEventJS(const nsAString& aType, bool aCanBubble,
+                              bool aCancelable, nsGlobalWindowInner* aView,
+                              bool aCtrlKey, bool aAltKey,
+                              bool aShiftKey, bool aMetaKey,
+                              uint32_t aKeyCode, uint32_t aCharCode)
 {
   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
+  mInitializedByJS = true;
+  mInitializedByCtor = false;
 
   UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
 
   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
   keyEvent->mKeyCode = aKeyCode;
   keyEvent->mCharCode = aCharCode;
 }
 
 void
-KeyboardEvent::InitKeyboardEvent(const nsAString& aType,
-                                 bool aCanBubble,
-                                 bool aCancelable,
-                                 nsGlobalWindowInner* aView,
-                                 const nsAString& aKey,
-                                 uint32_t aLocation,
-                                 bool aCtrlKey,
-                                 bool aAltKey,
-                                 bool aShiftKey,
-                                 bool aMetaKey,
-                                 ErrorResult& aRv)
+KeyboardEvent::InitKeyboardEventJS(const nsAString& aType,
+                                   bool aCanBubble,
+                                   bool aCancelable,
+                                   nsGlobalWindowInner* aView,
+                                   const nsAString& aKey,
+                                   uint32_t aLocation,
+                                   bool aCtrlKey,
+                                   bool aAltKey,
+                                   bool aShiftKey,
+                                   bool aMetaKey,
+                                   ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
+  mInitializedByJS = true;
+  mInitializedByCtor = false;
 
   UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, 0);
 
   WidgetKeyboardEvent* keyEvent = mEvent->AsKeyboardEvent();
   keyEvent->InitBasicModifiers(aCtrlKey, aAltKey, aShiftKey, aMetaKey);
   keyEvent->mLocation = aLocation;
   keyEvent->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   keyEvent->mKeyValue = aKey;
@@ -344,23 +408,23 @@ KeyboardEvent::GetDocument()
 
   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.
+  //   1. This event is initialized 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 ||
+  if (mInitializedByJS ||
       aCallerType == CallerType::System ||
       mEvent->mFlags.mInSystemGroup ||
       !nsContentUtils::ShouldResistFingerprinting() ||
       mEvent->AsKeyboardEvent()->mLocation ==
         KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
     return false;
   }
 
--- a/dom/events/KeyboardEvent.h
+++ b/dom/events/KeyboardEvent.h
@@ -24,17 +24,17 @@ public:
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(KeyboardEvent, UIEvent)
 
   virtual KeyboardEvent* AsKeyboardEvent() override
   {
     return this;
   }
 
-  static already_AddRefed<KeyboardEvent> Constructor(
+  static already_AddRefed<KeyboardEvent> ConstructorJS(
                                            const GlobalObject& aGlobal,
                                            const nsAString& aType,
                                            const KeyboardEventInit& aParam,
                                            ErrorResult& aRv);
 
   virtual JSObject*
     WrapObjectInternal(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override
@@ -58,45 +58,47 @@ public:
 
     Modifiers modifier = WidgetInputEvent::GetModifier(aKey);
     return GetSpoofedModifierStates(modifier, modifierState);
   }
 
   bool Repeat();
   bool IsComposing();
   void GetKey(nsAString& aKey) const;
-  uint32_t CharCode();
+  uint32_t CharCode(CallerType aCallerType = CallerType::System);
   uint32_t KeyCode(CallerType aCallerType = CallerType::System);
   virtual uint32_t Which(CallerType aCallerType = CallerType::System) override;
   uint32_t Location();
 
   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);
+  void InitKeyEventJS(const nsAString& aType, bool aCanBubble, bool aCancelable,
+                      nsGlobalWindowInner* aView, bool aCtrlKey, bool aAltKey,
+                      bool aShiftKey, bool aMetaKey,
+                      uint32_t aKeyCode, uint32_t aCharCode);
 
-  void InitKeyboardEvent(const nsAString& aType,
-                         bool aCanBubble, bool aCancelable,
-                         nsGlobalWindowInner* aView, const nsAString& aKey,
-                         uint32_t aLocation, bool aCtrlKey, bool aAltKey,
-                         bool aShiftKey, bool aMetaKey, ErrorResult& aRv);
+  void InitKeyboardEventJS(const nsAString& aType,
+                           bool aCanBubble, bool aCancelable,
+                           nsGlobalWindowInner* aView, const nsAString& aKey,
+                           uint32_t aLocation, bool aCtrlKey, bool aAltKey,
+                           bool aShiftKey, bool aMetaKey, ErrorResult& aRv);
 
 protected:
   ~KeyboardEvent() {}
 
   void InitWithKeyboardEventInit(EventTarget* aOwner,
                                  const nsAString& aType,
                                  const KeyboardEventInit& aParam,
                                  ErrorResult& aRv);
 
 private:
-  // True, if the instance is created with Constructor().
+  // True, if the instance is initialized by JS.
+  bool mInitializedByJS;
+  // True, if the instance is initialized by Ctor.
   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
@@ -108,16 +110,36 @@ private:
   // 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);
+
+  /**
+   * ComputeTraditionalKeyCode() computes traditional keyCode value.  I.e.,
+   * returns 0 if this event should return non-zero from CharCode().
+   * In spite of the name containing "traditional", this computes spoof
+   * keyCode value if user wants it.
+   *
+   * @param aKeyboardEvent  Should be |*mEvent->AsKeyboardEvent()|.
+   * @param aCallerType     Set caller type of KeyCode() or CharCode().
+   * @return                If traditional charCode value is 0, returns
+   *                        the raw keyCode value or spoof keyCode value.
+   *                        Otherwise, 0.
+   */
+  uint32_t ComputeTraditionalKeyCode(WidgetKeyboardEvent& aKeyboardEvent,
+                                     CallerType aCallerType);
+  /**
+   * ShouldUseSameValueForCharCodeAndKeyCode() returns true if KeyCode() and
+   * CharCode() should return same value.
+   */
+  bool ShouldUseSameValueForCharCodeAndKeyCode(CallerType aCallerType) const;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 already_AddRefed<mozilla::dom::KeyboardEvent>
 NS_NewDOMKeyboardEvent(mozilla::dom::EventTarget* aOwner,
                        nsPresContext* aPresContext,
--- a/dom/events/test/test_dom_keyboard_event.html
+++ b/dom/events/test/test_dom_keyboard_event.html
@@ -35,17 +35,17 @@ function testInitializingUntrustedEvent(
 
     { createEventArg: "keyboardevent", useInitKeyboardEvent: false,
       type: "keyup", bubbles: false, cancelable: true, view: window,
       ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
       keyCode: 0x10, charCode: 0x00, detail: 0, key: "", location: 0,
     }, // 1
 
     { createEventArg: "Keyboardevent", useInitKeyboardEvent: false,
-      type: "keypess", bubbles: true, cancelable: false, view: null,
+      type: "keypress", bubbles: true, cancelable: false, view: null,
       ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
       keyCode: 0x11, charCode: 0x30, detail: 0, key: "", location: 0,
     }, // 2
 
     { createEventArg: "keyboardEvent", useInitKeyboardEvent: false,
       type: "boo", bubbles: false, cancelable: false, view: window,
       ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
       keyCode: 0x30, charCode: 0x40, detail: 0, key: "", location: 0,
@@ -96,17 +96,17 @@ function testInitializingUntrustedEvent(
 
     { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
       type: "keyup", bubbles: false, cancelable: true, view: window,
       ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
       keyCode: 0x00, charCode: 0x00, key: "Unidentified", location: 1,
     }, // 11
 
     { createEventArg: "Keyboardevent", useInitKeyboardEvent: true,
-      type: "keypess", bubbles: true, cancelable: false, view: null,
+      type: "keypress", bubbles: true, cancelable: false, view: null,
       ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
       keyCode: 0x00, charCode: 0x00, key: "FooBar", location: 2,
     }, // 12
 
     { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
       type: "foo", bubbles: true, cancelable: true, view: null,
       ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
       keyCode: 0x00, charCode: 0x00, key: "a", location: 0,
--- a/dom/fetch/FetchConsumer.cpp
+++ b/dom/fetch/FetchConsumer.cpp
@@ -762,17 +762,25 @@ FetchBodyConsumer<Derived>::ContinueCons
   });
 
   if (aShuttingDown) {
     // If shutting down, we don't want to resolve any promise.
     return;
   }
 
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
-    localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    // Per https://fetch.spec.whatwg.org/#concept-read-all-bytes-from-readablestream
+    // Decoding errors should reject with a TypeError
+    if (aStatus == NS_ERROR_INVALID_CONTENT_ENCODING) {
+      IgnoredErrorResult rv;
+      rv.ThrowTypeError<MSG_DOM_DECODING_FAILED>();
+      localPromise->MaybeReject(rv);
+    } else {
+      localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    }
   }
 
   // Don't warn here since we warned above.
   if (NS_FAILED(aStatus)) {
     return;
   }
 
   // Finish successfully consuming body according to type.
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -1216,17 +1216,17 @@ FetchDriver::OnStopRequest(nsIRequest* a
   RefPtr<AlternativeDataStreamListener> altDataListener = mAltDataListener.forget();
 
   // We need to check mObserver, which is nulled by FailWithNetworkError(),
   // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
   // mResponse will definitely be null so we must not take the else branch.
   if (NS_FAILED(aStatusCode) || !mObserver) {
     nsCOMPtr<nsIAsyncOutputStream> outputStream = do_QueryInterface(mPipeOutputStream);
     if (outputStream) {
-      outputStream->CloseWithStatus(NS_BINDING_FAILED);
+      outputStream->CloseWithStatus(NS_FAILED(aStatusCode) ? aStatusCode : NS_BINDING_FAILED);
     }
     if (altDataListener) {
       altDataListener->Cancel();
     }
 
     // We proceed as usual here, since we've already created a successful response
     // from OnStartRequest.
   } else {
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -1380,17 +1380,17 @@ MediaRecorder::Stop(ErrorResult& aResult
   MOZ_ASSERT(mSessions.Length() > 0);
   mSessions.LastElement()->Stop();
 }
 
 void
 MediaRecorder::Pause(ErrorResult& aResult)
 {
   LOG(LogLevel::Debug, ("MediaRecorder.Pause"));
-  if (mState != RecordingState::Recording) {
+  if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   MOZ_ASSERT(mSessions.Length() > 0);
   nsresult rv = mSessions.LastElement()->Pause();
   if (NS_FAILED(rv)) {
     NotifyError(rv);
@@ -1398,17 +1398,17 @@ MediaRecorder::Pause(ErrorResult& aResul
   }
   mState = RecordingState::Paused;
 }
 
 void
 MediaRecorder::Resume(ErrorResult& aResult)
 {
   LOG(LogLevel::Debug, ("MediaRecorder.Resume"));
-  if (mState != RecordingState::Paused) {
+  if (mState == RecordingState::Inactive) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   MOZ_ASSERT(mSessions.Length() > 0);
   nsresult rv = mSessions.LastElement()->Resume();
   if (NS_FAILED(rv)) {
     NotifyError(rv);
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -679,42 +679,16 @@ ConvertToCdmEncryptionScheme(const GMPEn
     case GMPEncryptionScheme::kGMPEncryptionCbcs:
       return cdm::EncryptionScheme::kCbcs;
     default:
       MOZ_ASSERT_UNREACHABLE("Cannot convert invalid encryption scheme!");
       return cdm::EncryptionScheme::kUnencrypted;
   }
 }
 
-static cdm::EncryptionScheme
-ConvertToCdmEncryptionScheme(const GMPEncryptionScheme& aEncryptionScheme,
-                             uint64_t aNumCipherBytes)
-{
-  if (aNumCipherBytes == 0) {
-    // Starting at CDM10, if fed a sample marked as encrypted that has no
-    // encrypted bytes, the CDM will give a decryption error. So we mark these
-    // as unencrypted to attempt to avoid such errors -- though ideally our
-    // demuxers should not emit such data, so log it.
-    if (aEncryptionScheme != GMPEncryptionScheme::kGMPEncryptionNone) {
-      GMP_LOG(
-        "ChromiumCDMChild::ConvertToCdmEncryptionScheme() got scheme marked "
-        "as encrypted, but with no cipher bytes! This should be caught "
-        "earlier, preferably by the demuxer! Returning "
-        "cdm::EncryptionScheme::kUnencrypted");
-    }
-    return cdm::EncryptionScheme::kUnencrypted;
-  }
-  if (aEncryptionScheme == GMPEncryptionScheme::kGMPEncryptionNone) {
-    GMP_LOG("ChromiumCDMChild::ConvertToCdmEncryptionScheme() got scheme "
-            "marked as unecrypted but with > 0 cipher bytes! Something is "
-            "buggy to emit such data -- likey a demuxer");
-  }
-  return ConvertToCdmEncryptionScheme(aEncryptionScheme);
-}
-
 static void
 InitInputBuffer(const CDMInputBuffer& aBuffer,
                 nsTArray<cdm::SubsampleEntry>& aSubSamples,
                 cdm::InputBuffer_2& aInputBuffer)
 {
   aInputBuffer.data = aBuffer.mData().get<uint8_t>();
   aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
 
@@ -724,27 +698,25 @@ InitInputBuffer(const CDMInputBuffer& aB
     MOZ_ASSERT(aBuffer.mEncryptionScheme() ==
                GMPEncryptionScheme::kGMPEncryptionCenc);
     aInputBuffer.key_id = aBuffer.mKeyId().Elements();
     aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
 
     aInputBuffer.iv = aBuffer.mIV().Elements();
     aInputBuffer.iv_size = aBuffer.mIV().Length();
 
-    uint64_t numCipherBytes = 0;
     aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
     for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
       aSubSamples.AppendElement(cdm::SubsampleEntry{
         aBuffer.mClearBytes()[i], aBuffer.mCipherBytes()[i] });
-      numCipherBytes += aBuffer.mCipherBytes()[i];
     }
     aInputBuffer.subsamples = aSubSamples.Elements();
     aInputBuffer.num_subsamples = aSubSamples.Length();
     aInputBuffer.encryption_scheme =
-      ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme(), numCipherBytes);
+      ConvertToCdmEncryptionScheme(aBuffer.mEncryptionScheme());
   }
   aInputBuffer.timestamp = aBuffer.mTimestamp();
 }
 
 bool
 ChromiumCDMChild::HasShmemOfSize(size_t aSize) const
 {
   for (const ipc::Shmem& shmem : mBuffers) {
--- a/dom/webidl/KeyEvent.webidl
+++ b/dom/webidl/KeyEvent.webidl
@@ -225,16 +225,17 @@ interface KeyEvent
   const unsigned long DOM_VK_PLAY           = 0xFA;
   const unsigned long DOM_VK_ZOOM           = 0xFB;
   const unsigned long DOM_VK_PA1            = 0xFD;
 
   // OEM specific virtual keyCode of Windows should pass through DOM keyCode
   // for compatibility with the other web browsers on Windows.
   const unsigned long DOM_VK_WIN_OEM_CLEAR  = 0xFE;
 
+  [BinaryName="initKeyEventJS"]
   void initKeyEvent(DOMString type,
                     optional boolean canBubble = false,
                     optional boolean cancelable = false,
                     optional Window? view = null,
                     optional boolean ctrlKey = false,
                     optional boolean altKey = false,
                     optional boolean shiftKey = false,
                     optional boolean metaKey = false,
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -2,16 +2,17 @@
 /* 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/.
  */
 
 [Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)]
 interface KeyboardEvent : UIEvent
 {
+  [NeedsCallerType]
   readonly attribute unsigned long    charCode;
   [NeedsCallerType]
   readonly attribute unsigned long    keyCode;
 
   [NeedsCallerType]
   readonly attribute boolean          altKey;
   [NeedsCallerType]
   readonly attribute boolean          ctrlKey;
@@ -30,17 +31,17 @@ interface KeyboardEvent : UIEvent
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
   readonly attribute boolean       isComposing;
 
   readonly attribute DOMString key;
   [NeedsCallerType]
   readonly attribute DOMString code;
 
-  [Throws]
+  [Throws, BinaryName="initKeyboardEventJS"]
   void initKeyboardEvent(DOMString typeArg,
                          optional boolean bubblesArg = false,
                          optional boolean cancelableArg = false,
                          optional Window? viewArg = null,
                          optional DOMString keyArg = "",
                          optional unsigned long locationArg = 0,
                          optional boolean ctrlKey = false,
                          optional boolean altKey = false,
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -174,16 +174,30 @@ VARCACHE_PREF(
 #endif
 VARCACHE_PREF(
   "dom.animations-api.timelines.enabled",
    dom_animations_api_timelines_enabled,
   bool, PREF_VALUE
 )
 #undef PREF_VALUE
 
+// If this is true, "keypress" event's keyCode value and charCode value always
+// become same if the event is not created/initialized by JS.
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "dom.keyboardevent.keypress.set_keycode_and_charcode_to_same_value",
+   dom_keyboardevent_keypress_set_keycode_and_charcode_to_same_value,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 // NOTE: This preference is used in unit tests. If it is removed or its default
 // value changes, please update test_sharedMap_var_caches.js accordingly.
 VARCACHE_PREF(
   "dom.webcomponents.shadowdom.report_usage",
    dom_webcomponents_shadowdom_report_usage,
   bool, false
 )
 
deleted file mode 100644
--- a/testing/web-platform/meta/fetch/content-encoding/bad-gzip-body.any.js.ini
+++ /dev/null
@@ -1,33 +0,0 @@
-[bad-gzip-body.any.html]
-  [Consuming the body of a resource with bad gzip content with arrayBuffer() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with blob() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with formData() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with json() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with text() should reject]
-    expected: FAIL
-
-
-[bad-gzip-body.any.worker.html]
-  [Consuming the body of a resource with bad gzip content with arrayBuffer() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with blob() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with formData() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with json() should reject]
-    expected: FAIL
-
-  [Consuming the body of a resource with bad gzip content with text() should reject]
-    expected: FAIL
-
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -713,25 +713,45 @@ PuppetWidget::RequestIMEToCommitComposit
   Unused <<
     mTabChild->SendOnEventNeedingAckHandled(eCompositionCommitRequestHandled);
 
   // NOTE: PuppetWidget might be destroyed already.
   return NS_OK;
 }
 
 nsresult
-PuppetWidget::StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent,
+PuppetWidget::StartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
                              int32_t aPanelX, int32_t aPanelY,
                              nsString& aCommitted)
 {
+  DebugOnly<bool> propagationAlreadyStopped =
+    aKeyboardEvent.mFlags.mPropagationStopped;
+  DebugOnly<bool> immediatePropagationAlreadyStopped =
+    aKeyboardEvent.mFlags.mImmediatePropagationStopped;
   if (!mTabChild ||
       !mTabChild->SendStartPluginIME(aKeyboardEvent, aPanelX,
                                      aPanelY, &aCommitted)) {
     return NS_ERROR_FAILURE;
   }
+  // TabChild::SendStartPluginIME() sends back the keyboard event to the main
+  // process synchronously.  At this time, ParamTraits<WidgetEvent>::Write()
+  // marks the event as "posted to remote process".  However, this is not
+  // correct here since the event has been handled synchronously in the main
+  // process.  So, we adjust the cross process dispatching state here.
+  const_cast<WidgetKeyboardEvent&>(aKeyboardEvent).
+    ResetCrossProcessDispatchingState();
+  // Although it shouldn't occur in content process,
+  // ResetCrossProcessDispatchingState() may reset propagation state too
+  // if the event was posted to a remote process and we're waiting its
+  // result.  So, if you saw hitting the following assertions, you'd
+  // need to restore the propagation state too.
+  MOZ_ASSERT(propagationAlreadyStopped ==
+               aKeyboardEvent.mFlags.mPropagationStopped);
+  MOZ_ASSERT(immediatePropagationAlreadyStopped ==
+               aKeyboardEvent.mFlags.mImmediatePropagationStopped);
   return NS_OK;
 }
 
 void
 PuppetWidget::SetPluginFocused(bool& aFocused)
 {
   if (mTabChild) {
     mTabChild->SendSetPluginFocused(aFocused);