Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Sat, 03 Nov 2018 23:44:05 +0200
changeset 500765 cfdbecb2e230
parent 500748 50bad93c5bdf (current diff)
parent 500764 3eff39403c8c (diff)
child 500767 719fa010f362
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
cfdbecb2e230 / 65.0a1 / 20181103220126 / files
nightly linux64
cfdbecb2e230 / 65.0a1 / 20181103220126 / files
nightly mac
cfdbecb2e230 / 65.0a1 / 20181103220126 / files
nightly win32
cfdbecb2e230 / 65.0a1 / 20181103220126 / files
nightly win64
cfdbecb2e230 / 65.0a1 / 20181103220126 / 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 autoland to mozilla-central. a=merge
--- a/dom/base/test/test_warning_for_blocked_cross_site_request.html
+++ b/dom/base/test/test_warning_for_blocked_cross_site_request.html
@@ -4,68 +4,49 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=713980
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 713980</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
-  <!-- Load a cross-origin webfont without CORS (common pain point) and some
-       other styles that require anonymous CORS -->
+  <!-- Load a cross-origin webfont without CORS (common pain point -->
   <style>
     @font-face {
       font-family: "bad_cross_origin_webfont";
       src: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
     }
     div#bad_webfont { font-family: "bad_cross_origin_webfont"; }
-
-    div#bad_shape_outside { shape-outside: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=bad_shape_outside&type=image/png'); }
-
-    div#bad_mask_image { mask-image: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=bad_mask_image&type=image/svg+xml'); }
   </style>
 </head>
 <body>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 var tests = {
-  xhr : {
-    uri_test : "http://invalid",
-    result : null,
-    category: "CORSAllowOriginNotMatchingOrigin"
-  },
   font : {
     uri_test : "font_bad",
     result : null,
     category: "CORSMissingAllowOrigin",
   },
-  shape_outside : {
-    uri_test : "bad_shape_outside",
+  xhr : {
+    uri_test : "http://invalid",
     result : null,
-    category: "CORSMissingAllowOrigin",
-    ignore_windowID: true,
-  },
-  mask_image : {
-    uri_test : "bad_mask_image",
-    result : null,
-    category: "CORSMissingAllowOrigin",
-    ignore_windowID: true,
+    category: "CORSAllowOriginNotMatchingOrigin"
   },
 }
 
 function testsComplete() {
   for (var testName in tests) {
     var test = tests[testName];
-    if (test.result == null) {
-      info("Still waiting on (at least) " + testName + ".");
+    if (test.result == null)
       return false;
-    }
   }
   return true;
 }
 
 SpecialPowers.registerConsoleListener(function CORSMsgListener(aMsg) {
   if (!/Cross-Origin Request Blocked/.test(aMsg.message))
     return;
 
@@ -77,19 +58,17 @@ SpecialPowers.registerConsoleListener(fu
 
     var testRegexp = new RegExp(test.uri_test);
     if (testRegexp.test(aMsg.message)) {
       test.result = true;
       ok(true, "Got \"Cross-site request blocked\" warning message for " + testName);
       ok(aMsg.category == category,
          "Got warning message with category \"" + aMsg.category + "\", expected \"" + category + "\"");
       // Got the message we wanted - make sure it is destined for a valid inner window
-      if(!test.ignore_windowID) {
-        ok(aMsg.windowID != 0, "Valid (non-zero) windowID for the cross-site request blocked message.");
-      }
+      ok(aMsg.windowID != 0, "Valid (non-zero) windowID for the cross-site request blocked message.");
       break;
     }
   }
 
   if (testsComplete()) {
     SimpleTest.executeSoon(cleanup);
   }
 });
@@ -99,31 +78,19 @@ function cleanup() {
   SimpleTest.finish();
 }
 
 // Send a cross-origin XHR request without CORS
 var xhr = new XMLHttpRequest();
 xhr.open("GET", "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?allowOrigin=http://invalid", true);
 xhr.send(null);
 
-let badDiv;
-
 // Create a div that triggers a cross-origin webfont request
 // We do this in Javascript in order to guarantee the console listener has
 // already been registered; otherwise, there could be a race.
-badDiv = document.createElement('div');
+var badDiv = document.createElement('div');
 badDiv.setAttribute('id', 'bad_webfont');
 document.body.appendChild(badDiv);
-
-// Create a div that triggers a cross-origin request for a shape-outside image
-badDiv = document.createElement('div');
-badDiv.setAttribute('id', 'bad_shape_outside');
-document.body.appendChild(badDiv);
-
-// Create a div that triggers a cross-origin request for a mask-image
-badDiv = document.createElement('div');
-badDiv.setAttribute('id', 'bad_mask_image');
-document.body.appendChild(badDiv);
 </script>
 
 </pre>
 </body>
 </html>
--- a/dom/xbl/nsXBLService.cpp
+++ b/dom/xbl/nsXBLService.cpp
@@ -468,16 +468,31 @@ public:
   }
 
 private:
   Element* mElement;
   bool mHadData;
   bool* mResolveStyle;
 };
 
+static bool
+IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
+{
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_TRUE(uri, false);
+
+  bool isChrome = false;
+  return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
+}
+
 // This function loads a particular XBL file and installs all of the bindings
 // onto the element.
 nsresult
 nsXBLService::LoadBindings(Element* aElement, nsIURI* aURL,
                            nsIPrincipal* aOriginPrincipal,
                            nsXBLBinding** aBinding, bool* aResolveStyle)
 {
   MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal");
@@ -486,16 +501,55 @@ nsXBLService::LoadBindings(Element* aEle
   *aResolveStyle = false;
 
   AutoEnsureSubtreeStyled subtreeStyled(aElement);
 
   if (MOZ_UNLIKELY(!aURL)) {
     return NS_OK;
   }
 
+#ifdef DEBUG
+  // Ensures that only the whitelisted bindings are used in the following
+  // conditions:
+  //
+  // 1) In the content process
+  // 2) In a document that disallows XUL/XBL which only loads bindings
+  //    referenced in a chrome stylesheet.
+  //
+  // If the conditions are met, assert that:
+  //
+  // a) The binding is XMLPrettyPrint (since it may be bound to any XML)
+  // b) The binding is bound to one of the whitelisted element.
+  //
+  // The assertion might not catch all violations because (2) is needed
+  // for the current test setup. Someone may unknownly using a binding
+  // in AllowXULXBL() documents in content process in production without
+  // knowing.
+  if (XRE_IsContentProcess() &&
+      IsSystemOrChromeURLPrincipal(aOriginPrincipal) &&
+      aElement->OwnerDoc() && !aElement->OwnerDoc()->AllowXULXBL() &&
+      !aURL->GetSpecOrDefault().EqualsLiteral(
+       "chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint")) {
+    nsAtom* tag = aElement->NodeInfo()->NameAtom();
+    MOZ_ASSERT(
+      // datetimebox
+      tag == nsGkAtoms::datetimebox ||
+      // videocontrols
+      tag == nsGkAtoms::videocontrols ||
+      // pluginProblem
+      tag == nsGkAtoms::embed ||
+      tag == nsGkAtoms::applet ||
+      tag == nsGkAtoms::object ||
+      // xbl-marquee
+      tag == nsGkAtoms::marquee,
+      "Unexpected XBL binding used in the content process"
+    );
+  }
+#endif
+
   // Easy case: The binding was already loaded.
   nsXBLBinding* binding = aElement->GetXBLBinding();
   if (binding && !binding->MarkedForDeath() &&
       binding->PrototypeBinding()->CompareBindingURI(aURL)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> document = aElement->OwnerDoc();
@@ -871,31 +925,16 @@ nsXBLService::GetBinding(nsIContent* aBo
     }
 
     NS_ADDREF(*aResult = newBinding);
   }
 
   return NS_OK;
 }
 
-static bool
-IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
-{
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    return true;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aPrincipal->GetURI(getter_AddRefs(uri));
-  NS_ENSURE_TRUE(uri, false);
-
-  bool isChrome = false;
-  return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
-}
-
 nsresult
 nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
                                       nsIDocument* aBoundDocument,
                                       nsIURI* aBindingURI,
                                       nsIPrincipal* aOriginPrincipal,
                                       bool aForceSyncLoad,
                                       nsXBLDocumentInfo** aResult)
 {
--- a/editor/libeditor/EditAction.h
+++ b/editor/libeditor/EditAction.h
@@ -21,20 +21,21 @@ enum class EditAction
   // something at creating, destroying or focus move etc, i.e., not edit
   // action is being handled but editor is doing something.
   eNotEditing,
 
   // eInsertText indicates to insert some characters.
   eInsertText,
 
   // eInsertParagraphSeparator indicates to insert a paragraph separator such
-  // as <p>, <div> or just \n in TextEditor.
+  // as <p>, <div>.
   eInsertParagraphSeparator,
 
-  // eInsertLineBreak indicates to insert a <br> element in HTMLEditor.
+  // eInsertLineBreak indicates to insert \n into TextEditor or a <br> element
+  // in HTMLEditor.
   eInsertLineBreak,
 
   // eDeleteSelection indicates to delete selected content or content around
   // caret if selection is collapsed.
   eDeleteSelection,
 
   // eDeleteBackward indicates to remove previous character element of caret.
   // This may be set even when Selection is not collapsed.
@@ -393,18 +394,22 @@ enum class EditSubAction : int32_t
 
   // eComputeTextToOutput indicates to compute the editor value as plain text
   // or something requested format.
   eComputeTextToOutput,
 
   // eSetText indicates to set editor value to new value.
   eSetText,
 
+  // eInsertLineBreak indicates to insert a line break, <br> or \n to break
+  // current line.
+  eInsertLineBreak,
+
   // eInsertParagraphSeparator indicates to insert paragraph separator, <br> or
-  // \n at least to break current line.
+  // \n at least to break current line in HTMLEditor.
   eInsertParagraphSeparator,
 
   // eCreateOrChangeList indicates to create new list or change existing list
   // type.
   eCreateOrChangeList,
 
   // eIndent and eOutdent indicates to indent or outdent the target with
   // using <blockquote>, <ul>, <ol> or just margin of start edge.
--- a/editor/libeditor/EditorCommands.cpp
+++ b/editor/libeditor/EditorCommands.cpp
@@ -1173,21 +1173,21 @@ NS_IMETHODIMP
 InsertParagraphCommand::DoCommand(const char* aCommandName,
                                   nsISupports* aCommandRefCon)
 {
   nsCOMPtr<nsIEditor> editor = do_QueryInterface(aCommandRefCon);
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
 
-  TextEditor* textEditor = editor->AsTextEditor();
-  MOZ_ASSERT(textEditor);
-  // XXX OnInputParagraphSeparator() is a handler of user input.  So, this
-  //     call may not be expected.
-  return textEditor->OnInputParagraphSeparator();
+  HTMLEditor* htmlEditor = editor->AsHTMLEditor();
+  if (!htmlEditor) {
+    return NS_OK; // Do nothing for now.
+  }
+  return htmlEditor->InsertParagraphSeparatorAsAction();
 }
 
 NS_IMETHODIMP
 InsertParagraphCommand::DoCommandParams(const char* aCommandName,
                                         nsICommandParams* aParams,
                                         nsISupports* aCommandRefCon)
 {
   return DoCommand(aCommandName, aCommandRefCon);
@@ -1239,19 +1239,17 @@ InsertLineBreakCommand::DoCommand(const 
   if (NS_WARN_IF(!editor)) {
     return NS_ERROR_FAILURE;
   }
 
   HTMLEditor* htmlEditor = editor->AsHTMLEditor();
   if (!htmlEditor) {
     return NS_ERROR_FAILURE;
   }
-  // XXX OnInputLineBreak() is a handler of user input.  So, this call may not
-  //     be expected.
-  return htmlEditor->OnInputLineBreak();
+  return htmlEditor->InsertLineBreakAsAction();
 }
 
 NS_IMETHODIMP
 InsertLineBreakCommand::DoCommandParams(const char* aCommandName,
                                         nsICommandParams* aParams,
                                         nsISupports* aCommandRefCon)
 {
   return DoCommand(aCommandName, aCommandRefCon);
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -43,16 +43,17 @@ class Text;
 class MOZ_STACK_CLASS EditActionResult final
 {
 public:
   bool Succeeded() const { return NS_SUCCEEDED(mRv); }
   bool Failed() const { return NS_FAILED(mRv); }
   nsresult Rv() const { return mRv; }
   bool Canceled() const { return mCanceled; }
   bool Handled() const { return mHandled; }
+  bool Ignored() const { return !mCanceled && !mHandled; }
   bool EditorDestroyed() const { return mRv == NS_ERROR_EDITOR_DESTROYED; }
 
   EditActionResult SetResult(nsresult aRv)
   {
     mRv = aRv;
     return *this;
   }
   EditActionResult MarkAsCanceled()
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -80,16 +80,17 @@ enum
 /********************************************************
  *  first some helpful functors we will use
  ********************************************************/
 
 static bool
 IsStyleCachePreservingSubAction(EditSubAction aEditSubAction)
 {
   return aEditSubAction == EditSubAction::eDeleteSelectedContent ||
+         aEditSubAction == EditSubAction::eInsertLineBreak ||
          aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
          aEditSubAction == EditSubAction::eCreateOrChangeList ||
          aEditSubAction == EditSubAction::eIndent ||
          aEditSubAction == EditSubAction::eOutdent ||
          aEditSubAction == EditSubAction::eSetOrClearAlignment ||
          aEditSubAction == EditSubAction::eCreateOrRemoveBlock ||
          aEditSubAction == EditSubAction::eRemoveList ||
          aEditSubAction == EditSubAction::eCreateOrChangeDefinitionList ||
@@ -556,16 +557,17 @@ HTMLEditRules::AfterEditInner(EditSubAct
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // attempt to transform any unneeded nbsp's into spaces after doing various operations
     if (aEditSubAction == EditSubAction::eInsertText ||
         aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
         aEditSubAction == EditSubAction::eDeleteSelectedContent ||
+        aEditSubAction == EditSubAction::eInsertLineBreak ||
         aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
         aEditSubAction == EditSubAction::ePasteHTMLContent ||
         aEditSubAction == EditSubAction::eInsertHTMLSource) {
       rv = AdjustWhitespace();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
@@ -592,16 +594,17 @@ HTMLEditRules::AfterEditInner(EditSubAct
         "Failed to pin selection to the new block");
       mNewBlock = nullptr;
     }
 
     // adjust selection for insert text, html paste, and delete actions
     if (aEditSubAction == EditSubAction::eInsertText ||
         aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
         aEditSubAction == EditSubAction::eDeleteSelectedContent ||
+        aEditSubAction == EditSubAction::eInsertLineBreak ||
         aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
         aEditSubAction == EditSubAction::ePasteHTMLContent ||
         aEditSubAction == EditSubAction::eInsertHTMLSource) {
       rv = AdjustSelection(aDirection);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
@@ -711,19 +714,27 @@ HTMLEditRules::WillDoAction(EditSubActio
     case EditSubAction::eInsertText:
     case EditSubAction::eInsertTextComingFromIME:
       UndefineCaretBidiLevel();
       return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
                             aInfo.inString, aInfo.outString,
                             aInfo.maxLength);
     case EditSubAction::eInsertHTMLSource:
       return WillLoadHTML();
-    case EditSubAction::eInsertParagraphSeparator:
+    case EditSubAction::eInsertParagraphSeparator: {
       UndefineCaretBidiLevel();
-      return WillInsertBreak(aCancel, aHandled);
+      EditActionResult result = WillInsertParagraphSeparator();
+      if (NS_WARN_IF(result.Failed())) {
+        return result.Rv();
+      }
+      *aCancel = result.Canceled();
+      *aHandled = result.Handled();
+      MOZ_ASSERT(!result.Ignored());
+      return NS_OK;
+    }
     case EditSubAction::eDeleteSelectedContent:
       return WillDeleteSelection(aInfo.collapsedAction, aInfo.stripWrappers,
                                  aCancel, aHandled);
     case EditSubAction::eCreateOrChangeList:
       return WillMakeList(aInfo.blockType, aInfo.entireList,
                           aInfo.bulletType, aCancel, aHandled);
     case EditSubAction::eIndent:
       return WillIndent(aCancel, aHandled);
@@ -775,16 +786,17 @@ HTMLEditRules::DidDoAction(EditSubAction
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor);
 
   switch (aInfo.mEditSubAction) {
     case EditSubAction::eInsertText:
+    case EditSubAction::eInsertLineBreak:
     case EditSubAction::eInsertParagraphSeparator:
     case EditSubAction::eInsertTextComingFromIME:
       return NS_OK;
     case EditSubAction::eDeleteSelectedContent:
       return DidDeleteSelection();
     case EditSubAction::eCreateOrRemoveBlock:
     case EditSubAction::eIndent:
     case EditSubAction::eOutdent:
@@ -1691,82 +1703,76 @@ HTMLEditRules::CanContainParagraph(Eleme
     return true;
   }
 
   // XXX Otherwise, Chromium checks the CSS box is a block, but we don't do it
   //     for now.
   return false;
 }
 
-nsresult
-HTMLEditRules::WillInsertBreak(bool* aCancel,
-                               bool* aHandled)
-{
-  MOZ_ASSERT(IsEditorDataAvailable());
-
-  MOZ_ASSERT(aCancel && aHandled);
-  *aCancel = false;
-  *aHandled = false;
+EditActionResult
+HTMLEditRules::WillInsertParagraphSeparator()
+{
+  MOZ_ASSERT(IsEditorDataAvailable());
 
   // If the selection isn't collapsed, delete it.
   if (!SelectionRefPtr()->IsCollapsed()) {
     nsresult rv =
       HTMLEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
                                                  nsIEditor::eStrip);
     if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      return EditActionIgnored(rv);
     }
   }
 
   // FYI: Ignore cancel result of WillInsert().
   nsresult rv = WillInsert();
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-    return NS_ERROR_EDITOR_DESTROYED;
+    return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
 
   // Split any mailcites in the way.  Should we abort this if we encounter
   // table cell boundaries?
   if (IsMailEditor()) {
-    nsresult rv = SplitMailCites(aHandled);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    if (*aHandled) {
-      return NS_OK;
+    EditActionResult result = SplitMailCites();
+    if (NS_WARN_IF(result.Failed())) {
+      return result;
+    }
+    if (result.Handled()) {
+      return result;
     }
   }
 
   // Smart splitting rules
   nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
   if (NS_WARN_IF(!firstRange)) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   EditorDOMPoint atStartOfSelection(firstRange->StartRef());
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
   MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
 
   // Do nothing if the node is read-only
   if (!HTMLEditorRef().IsModifiableNode(*atStartOfSelection.GetContainer())) {
-    *aCancel = true;
-    return NS_OK;
+    return EditActionCanceled();
   }
 
   // If the active editing host is an inline element, or if the active editing
   // host is the block parent itself and we're configured to use <br> as a
   // paragraph separator, just append a <br>.
   RefPtr<Element> host = HTMLEditorRef().GetActiveEditingHost();
   if (NS_WARN_IF(!host)) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   // Look for the nearest parent block.  However, don't return error even if
   // there is no block parent here because in such case, i.e., editing host
   // is an inline element, we should insert <br> simply.
   RefPtr<Element> blockParent =
     HTMLEditor::GetBlock(*atStartOfSelection.GetContainer(), host);
 
@@ -1802,63 +1808,61 @@ HTMLEditRules::WillInsertBreak(bool* aCa
     }
   }
 
   // If we cannot insert a <p>/<div> element at the selection, we should insert
   // a <br> element instead.
   if (insertBRElement) {
     nsresult rv = InsertBRElement(atStartOfSelection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    *aHandled = true;
-    return NS_OK;
+      return EditActionIgnored(rv);
+    }
+    return EditActionHandled();
   }
 
   if (host == blockParent && separator != ParagraphSeparator::br) {
     // Insert a new block first
     MOZ_ASSERT(separator == ParagraphSeparator::div ||
                separator == ParagraphSeparator::p);
     // MakeBasicBlock() creates AutoSelectionRestorer.
     // Therefore, even if it returns NS_OK, editor might have been destroyed
     // at restoring Selection.
     nsresult rv = MakeBasicBlock(ParagraphSeparatorElement(separator));
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
         NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     // We warn on failure, but don't handle it, because it might be harmless.
     // Instead we just check that a new block was actually created.
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditRules::MakeBasicBlock() failed");
 
     firstRange = SelectionRefPtr()->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
-      return NS_ERROR_FAILURE;
+      return EditActionIgnored(NS_ERROR_FAILURE);
     }
 
     atStartOfSelection = firstRange->StartRef();
     if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
-      return NS_ERROR_FAILURE;
+      return EditActionIgnored(NS_ERROR_FAILURE);
     }
     MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
 
     blockParent =
       HTMLEditor::GetBlock(*atStartOfSelection.GetContainer(), host);
     if (NS_WARN_IF(!blockParent)) {
-      return NS_ERROR_UNEXPECTED;
+      return EditActionIgnored(NS_ERROR_UNEXPECTED);
     }
     if (NS_WARN_IF(blockParent == host)) {
       // Didn't create a new block for some reason, fall back to <br>
       rv = InsertBRElement(atStartOfSelection);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-      *aHandled = true;
-      return NS_OK;
+        return EditActionIgnored(rv);
+      }
+      return EditActionHandled();
     }
     // Now, mNewBlock is last created block element for wrapping inline
     // elements around the caret position and AfterEditInner() will move
     // caret into it.  However, it may be different from block parent of
     // the caret position.  E.g., MakeBasicBlock() may wrap following
     // inline elements of a <br> element which is next sibling of container
     // of the caret.  So, we need to adjust mNewBlock here for avoiding
     // jumping caret to odd position.
@@ -1871,49 +1875,47 @@ HTMLEditRules::WillInsertBreak(bool* aCa
   // make block have a line.  Then code further below will put in a second br.)
   if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
     AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
     EditorRawDOMPoint endOfBlockParent;
     endOfBlockParent.SetToEndOf(blockParent);
     RefPtr<Element> brElement =
       HTMLEditorRef().InsertBrElementWithTransaction(endOfBlockParent);
     if (NS_WARN_IF(!CanHandleEditAction())) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(!brElement)) {
-      return NS_ERROR_FAILURE;
+      return EditActionIgnored(NS_ERROR_FAILURE);
     }
   }
 
   nsCOMPtr<Element> listItem = IsInListItem(blockParent);
   if (listItem && listItem != host) {
     nsresult rv =
       ReturnInListItem(*listItem, *atStartOfSelection.GetContainer(),
                        atStartOfSelection.Offset());
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert break into list item");
-    *aHandled = true;
-    return NS_OK;
+    return EditActionHandled();
   }
 
   if (HTMLEditUtils::IsHeader(*blockParent)) {
     // Headers: close (or split) header
     nsresult rv =
       ReturnInHeader(*blockParent, *atStartOfSelection.GetContainer(),
                      atStartOfSelection.Offset());
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to handle insertParagraph in the heading element");
-    *aHandled = true;
-    return NS_OK;
+    return EditActionHandled();
   }
 
   // XXX Ideally, we should take same behavior with both <p> container and
   //     <div> container.  However, we are still using <br> as default
   //     paragraph separator (non-standard) and we've split only <p> container
   //     long time.  Therefore, some web apps may depend on this behavior like
   //     Gmail.  So, let's use traditional odd behavior only when the default
   //     paragraph separator is <br>.  Otherwise, take consistent behavior
@@ -1921,41 +1923,37 @@ HTMLEditRules::WillInsertBreak(bool* aCa
   if ((separator == ParagraphSeparator::br &&
        blockParent->IsHTMLElement(nsGkAtoms::p)) ||
       (separator != ParagraphSeparator::br &&
        blockParent->IsAnyOfHTMLElements(nsGkAtoms::p, nsGkAtoms::div))) {
     AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
     // Paragraphs: special rules to look for <br>s
     EditActionResult result = ReturnInParagraph(*blockParent);
     if (NS_WARN_IF(result.Failed())) {
-      return result.Rv();
-    }
-    *aHandled = result.Handled();
-    *aCancel = result.Canceled();
+      return result;
+    }
     if (result.Handled()) {
       // Now, atStartOfSelection may be invalid because the left paragraph
       // may have less children than its offset.  For avoiding warnings of
       // validation of EditorDOMPoint, we should not touch it anymore.
       lockOffset.Cancel();
-      return NS_OK;
+      return result;
     }
     // Fall through, if ReturnInParagraph() didn't handle it.
-    MOZ_ASSERT(!*aCancel, "ReturnInParagraph canceled this edit action, "
-                          "WillInsertBreak() needs to handle such case");
+    MOZ_ASSERT(!result.Canceled(),
+               "ReturnInParagraph canceled this edit action, "
+               "WillInsertBreak() needs to handle such case");
   }
 
   // If nobody handles this edit action, let's insert new <br> at the selection.
-  MOZ_ASSERT(!*aHandled, "Reached last resort of WillInsertBreak() "
-                         "after the edit action is handled");
   rv = InsertBRElement(atStartOfSelection);
-  *aHandled = true;
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
+    return EditActionIgnored(rv);
+  }
+  return EditActionHandled();
 }
 
 nsresult
 HTMLEditRules::InsertBRElement(const EditorDOMPoint& aPointToBreak)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (NS_WARN_IF(!aPointToBreak.IsSet())) {
@@ -2096,34 +2094,30 @@ HTMLEditRules::InsertBRElement(const Edi
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
   return NS_OK;
 }
 
-nsresult
-HTMLEditRules::SplitMailCites(bool* aHandled)
-{
-  MOZ_ASSERT(IsEditorDataAvailable());
-
-  if (NS_WARN_IF(!aHandled)) {
-    return NS_ERROR_INVALID_ARG;
-  }
+EditActionResult
+HTMLEditRules::SplitMailCites()
+{
+  MOZ_ASSERT(IsEditorDataAvailable());
 
   EditorRawDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!pointToSplit.IsSet())) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   RefPtr<Element> citeNode =
     GetTopEnclosingMailCite(*pointToSplit.GetContainer());
   if (!citeNode) {
-    return NS_OK;
+    return EditActionIgnored();
   }
 
   // If our selection is just before a break, nudge it to be just after it.
   // This does two things for us.  It saves us the trouble of having to add
   // a break here ourselves to preserve the "blockness" of the inline span
   // mailquote (in the inline case), and :
   // it means the break won't end up making an empty line that happens to be
   // inside a mailquote (in either inline or block case).
@@ -2140,28 +2134,28 @@ HTMLEditRules::SplitMailCites(bool* aHan
       visNode != citeNode && citeNode->Contains(visNode)) {
     pointToSplit.Set(visNode);
     DebugOnly<bool> advanced = pointToSplit.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
       "Failed to advance offset to after the visible node");
   }
 
   if (NS_WARN_IF(!pointToSplit.GetContainerAsContent())) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
 
   SplitNodeResult splitCiteNodeResult =
     HTMLEditorRef().SplitNodeDeepWithTransaction(
                       *citeNode, pointToSplit,
                       SplitAtEdges::eDoNotCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
+    return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(splitCiteNodeResult.Failed())) {
-    return splitCiteNodeResult.Rv();
+    return EditActionIgnored(splitCiteNodeResult.Rv());
   }
   pointToSplit.Clear();
 
   // Add an invisible <br> to the end of current cite node (If new left cite
   // has not been created, we're at the end of it.  Otherwise, we're still at
   // the right node) if it was a <span> of style="display: block". This is
   // important, since when serializing the cite to plain text, the span which
   // caused the visual break is discarded.  So the added <br> will guarantee
@@ -2181,53 +2175,53 @@ HTMLEditRules::SplitMailCites(bool* aHan
     if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
       // We ignore the result here.
       EditorRawDOMPoint endOfPreviousNodeOfSplitPoint;
       endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
       RefPtr<Element> invisibleBrElement =
         HTMLEditorRef().InsertBrElementWithTransaction(
                           endOfPreviousNodeOfSplitPoint);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       NS_WARNING_ASSERTION(invisibleBrElement,
         "Failed to create an invisible <br> element");
     }
   }
 
   // In most cases, <br> should be inserted after current cite.  However, if
   // left cite hasn't been created because the split point was start of the
   // cite node, <br> should be inserted before the current cite.
   EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
   RefPtr<Element> brElement =
     HTMLEditorRef().InsertBrElementWithTransaction(pointToInsertBrNode);
   if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
+    return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(!brElement)) {
-    return NS_ERROR_FAILURE;
+    return EditActionIgnored(NS_ERROR_FAILURE);
   }
   // Now, offset of pointToInsertBrNode is invalid.  Let's clear it.
   pointToInsertBrNode.Clear();
 
   // Want selection before the break, and on same line.
   EditorDOMPoint atBrNode(brElement);
   Unused << atBrNode.Offset(); // Needs offset after collapsing the selection.
   ErrorResult error;
   SelectionRefPtr()->SetInterlinePosition(true, error);
   NS_WARNING_ASSERTION(!error.Failed(),
     "Failed to set interline position");
   error = NS_OK;
   SelectionRefPtr()->Collapse(atBrNode, error);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     error.SuppressException();
-    return NS_ERROR_EDITOR_DESTROYED;
+    return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(error.Failed())) {
-    return error.StealNSResult();
+    return EditActionIgnored(error.StealNSResult());
   }
 
   // if citeNode wasn't a block, we might also want another break before it.
   // We need to examine the content both before the br we just added and also
   // just after it.  If we don't have another br or block boundary adjacent,
   // then we will need a 2nd br added to achieve blank line that user expects.
   if (IsInlineNode(*citeNode)) {
     // Use DOM point which we tried to collapse to.
@@ -2249,67 +2243,66 @@ HTMLEditRules::SplitMailCites(bool* aHan
       if (wsType == WSType::normalWS || wsType == WSType::text ||
           wsType == WSType::special ||
           // In case we're at the very end.
           wsType == WSType::thisBlock) {
         brElement =
           HTMLEditorRef().InsertBrElementWithTransaction(
                             pointToCreateNewBrNode);
         if (NS_WARN_IF(!CanHandleEditAction())) {
-          return NS_ERROR_EDITOR_DESTROYED;
+          return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(!brElement)) {
-          return NS_ERROR_FAILURE;
+          return EditActionIgnored(NS_ERROR_FAILURE);
         }
         // Now, those points may be invalid.
         pointToCreateNewBrNode.Clear();
         pointAfterNewBrNode.Clear();
       }
     }
   }
 
   // delete any empty cites
   bool bEmptyCite = false;
   if (previousNodeOfSplitPoint) {
     nsresult rv =
       HTMLEditorRef().IsEmptyNode(previousNodeOfSplitPoint, &bEmptyCite,
                                   true, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      return EditActionIgnored(rv);
     }
     if (bEmptyCite) {
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*previousNodeOfSplitPoint);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+        return EditActionIgnored(rv);
       }
     }
   }
 
   if (citeNode) {
     nsresult rv =
       HTMLEditorRef().IsEmptyNode(citeNode, &bEmptyCite, true, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      return EditActionIgnored(rv);
     }
     if (bEmptyCite) {
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*citeNode);
       if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
+        return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-  }
-
-  *aHandled = true;
-  return NS_OK;
+        return EditActionIgnored(rv);
+      }
+    }
+  }
+
+  return EditActionHandled();
 }
 
 
 nsresult
 HTMLEditRules::WillDeleteSelection(nsIEditor::EDirection aAction,
                                    nsIEditor::EStripWrappers aStripWrappers,
                                    bool* aCancel,
                                    bool* aHandled)
@@ -7010,16 +7003,17 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
                                 EditSubAction aEditSubAction)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // we do one thing for text actions, something else entirely for other
   // actions
   if (aEditSubAction == EditSubAction::eInsertText ||
       aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
+      aEditSubAction == EditSubAction::eInsertLineBreak ||
       aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
       aEditSubAction == EditSubAction::eDeleteText) {
     bool isSpace, isNBSP;
     nsCOMPtr<nsIContent> content =
       aNode.IsContent() ? aNode.AsContent() : nullptr;
     nsCOMPtr<nsIContent> temp;
     int32_t newOffset = aOffset;
     // for text actions, we want to look backwards (or forwards, as
@@ -7260,16 +7254,17 @@ HTMLEditRules::PromoteRange(nsRange& aRa
         startOffset = 0;
         endOffset = block->Length();
       }
     }
   }
 
   if (aEditSubAction == EditSubAction::eInsertText ||
       aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
+      aEditSubAction == EditSubAction::eInsertLineBreak ||
       aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
       aEditSubAction == EditSubAction::eDeleteText) {
      if (!startNode->IsContent() ||
          !endNode->IsContent()) {
        // GetPromotedPoint cannot promote node when action type is text
        // operation and selected node isn't content node.
        return;
      }
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -95,16 +95,17 @@ public:
 
   // TextEditRules methods
   virtual nsresult Init(TextEditor* aTextEditor) override;
   virtual nsresult DetachEditor() override;
   virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
                               nsIEditor::EDirection aDirection) override;
   virtual nsresult AfterEdit(EditSubAction aEditSubAction,
                              nsIEditor::EDirection aDirection) override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult WillDoAction(EditSubActionInfo& aInfo,
                                 bool* aCancel,
                                 bool* aHandled) override;
   virtual nsresult DidDoAction(EditSubActionInfo& aInfo,
                                nsresult aResult) override;
   virtual bool DocumentIsEmpty() override;
 
   /**
@@ -207,24 +208,22 @@ protected:
 
   /**
    * WillLoadHTML() is called before loading enter document from source.
    * This removes bogus node if there is.
    */
   MOZ_MUST_USE nsresult WillLoadHTML();
 
   /**
-   * WillInsertBreak() is called when insertParagraph command is executed
-   * or something equivalent.  This method actually tries to insert new
-   * paragraph or <br> element, etc.
-   *
-   * @param aCancel             Returns true if target node is not editable.
-   * @param aHandled            Returns true if actually insert new break.
+   * WillInsertParagraphSeparator() is called when insertParagraph command is
+   * executed or something equivalent.  This method actually tries to insert
+   * new paragraph or <br> element, etc.
    */
-  nsresult WillInsertBreak(bool* aCancel, bool* aHandled);
+  MOZ_CAN_RUN_SCRIPT
+  MOZ_MUST_USE EditActionResult WillInsertParagraphSeparator();
 
   /**
    * If aNode is a text node that contains only collapsed whitespace, delete
    * it.  It doesn't serve any useful purpose, and we don't want it to confuse
    * code that doesn't correctly skip over it.
    *
    * If deleting the node fails (like if it's not editable), the caller should
    * proceed as usual, so don't return any errors.
@@ -239,21 +238,19 @@ protected:
    */
   MOZ_MUST_USE nsresult InsertBRElement(const EditorDOMPoint& aInsertToBreak);
 
   /**
    * SplitMailCites() splits mail-cite elements at start of Selection if
    * Selection starts from inside a mail-cite element.  Of course, if it's
    * necessary, this inserts <br> node to new left nodes or existing right
    * nodes.
-   *
-   * @param aHandled            Returns true if succeeded to split mail-cite
-   *                            elements.
    */
-  MOZ_MUST_USE nsresult SplitMailCites(bool* aHandled);
+  MOZ_CAN_RUN_SCRIPT
+  MOZ_MUST_USE EditActionResult SplitMailCites();
 
   /**
    * Called before deleting selected contents.  This method actually removes
    * selected contents.
    *
    * @param aAction             Direction of the deletion.
    * @param aStripWrappers      Must be eStrip or eNoStrip.
    * @param aCancel             Returns true if the operation is canceled.
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -866,20 +866,20 @@ HTMLEditor::HandleKeyPressEvent(WidgetKe
     }
     case NS_VK_RETURN:
       if (!aKeyboardEvent->IsInputtingLineBreak()) {
         return NS_OK;
       }
       aKeyboardEvent->PreventDefault(); // consumed
       if (aKeyboardEvent->IsShift()) {
         // Only inserts a <br> element.
-        return OnInputLineBreak();
+        return InsertLineBreakAsAction();
       }
       // uses rules to figure out what to insert
-      return OnInputParagraphSeparator();
+      return InsertParagraphSeparatorAsAction();
   }
 
   if (!aKeyboardEvent->IsInputtingText()) {
     // we don't PreventDefault() here or keybindings like control-x won't work
     return NS_OK;
   }
   aKeyboardEvent->PreventDefault();
   nsAutoString str(aKeyboardEvent->mCharCode);
@@ -1117,33 +1117,114 @@ HTMLEditor::UpdateBaseURL()
   // If no base tag, then set baseURL to the document's URL.  This is very
   // important, else relative URLs for links and images are wrong
   if (!nodeList || !nodeList->Item(0)) {
     doc->SetBaseURI(doc->GetDocumentURI());
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HTMLEditor::InsertLineBreak()
+{
+  MOZ_ASSERT(!IsSingleLineEditor());
+
+  // XPCOM method's InsertLineBreak() should insert paragraph separator in
+  // HTMLEditor.
+  AutoEditActionDataSetter editActionData(
+                             *this,
+                             EditAction::eInsertParagraphSeparator);
+  if (NS_WARN_IF(!editActionData.CanHandle())) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  // XXX This method is called by chrome of comm-central.  So, using
+  //     TypingTxnName here is odd in such case.
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
+  nsresult rv = InsertParagraphSeparatorAsSubAction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 nsresult
-HTMLEditor::OnInputLineBreak()
+HTMLEditor::InsertLineBreakAsAction()
 {
   AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
+  // XXX This method may be called by "insertLineBreak" command.  So, using
+  //     TypingTxnName here is odd in such case.
   AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertBrElementAtSelectionWithTransaction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
+HTMLEditor::InsertParagraphSeparatorAsAction()
+{
+  AutoEditActionDataSetter editActionData(
+                             *this,
+                             EditAction::eInsertParagraphSeparator);
+  if (NS_WARN_IF(!editActionData.CanHandle())) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  // XXX This may be called by execCommand() with "insertParagraph".
+  //     In such case, naming the transaction "TypingTxnName" is odd.
+  AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
+  nsresult rv = InsertParagraphSeparatorAsSubAction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+HTMLEditor::InsertParagraphSeparatorAsSubAction()
+{
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (!mRules) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  // Protect the edit rules object from dying
+  RefPtr<TextEditRules> rules(mRules);
+
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this,
+                                      EditSubAction::eInsertParagraphSeparator,
+                                      nsIEditor::eNext);
+
+  EditSubActionInfo subActionInfo(EditSubAction::eInsertParagraphSeparator);
+  subActionInfo.maxLength = mMaxTextLength;
+  bool cancel, handled;
+  nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
+  if (cancel) {
+    return rv; // We don't need to call DidDoAction() if canceled.
+  }
+  // XXX DidDoAction() does nothing for eInsertParagraphSeparator.  However,
+  //     we should call it until we keep using this style.  Perhaps, each
+  //     editor method should call necessary method of
+  //     TextEditRules/HTMLEditRules directly.
+  rv = rules->DidDoAction(subActionInfo, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
 HTMLEditor::TabInTable(bool inIsShift,
                        bool* outHandled)
 {
   NS_ENSURE_TRUE(outHandled, NS_ERROR_NULL_POINTER);
   *outHandled = false;
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -1239,17 +1320,17 @@ HTMLEditor::TabInTable(bool inIsShift,
 
 nsresult
 HTMLEditor::InsertBrElementAtSelectionWithTransaction()
 {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   // calling it text insertion to trigger moz br treatment by rules
   // XXX Why do we use EditSubAction::eInsertText here?  Looks like
-  //     EditSubAction::eInsertParagraphSeparator or EditSubAction::eInsertNode
+  //     EditSubAction::eInsertLineBreak or EditSubAction::eInsertNode
   //     is better.
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertText,
                                       nsIEditor::eNext);
 
   if (!SelectionRefPtr()->IsCollapsed()) {
     nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -134,16 +134,18 @@ public:
   NS_IMETHOD SetFlags(uint32_t aFlags) override;
 
   NS_IMETHOD CanPaste(int32_t aSelectionType, bool* aCanPaste) override;
 
   NS_IMETHOD PasteTransferable(nsITransferable* aTransferable) override;
 
   NS_IMETHOD DeleteNode(nsINode* aNode) override;
 
+  NS_IMETHOD InsertLineBreak() override;
+
   virtual nsresult HandleKeyPressEvent(
                      WidgetKeyboardEvent* aKeyboardEvent) override;
   virtual nsIContent* GetFocusedContent() override;
   virtual already_AddRefed<nsIContent> GetFocusedContentForIME() override;
   virtual bool IsActiveInDOMWindow() override;
   virtual dom::EventTarget* GetDOMEventTarget() override;
   virtual Element* FindSelectionRoot(nsINode *aNode) const override;
   virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) override;
@@ -166,20 +168,26 @@ public:
    * Can we paste |aTransferable| or, if |aTransferable| is null, will a call
    * to pasteTransferable later possibly succeed if given an instance of
    * nsITransferable then? True if the doc is modifiable, and, if
    * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|.
    */
   virtual bool CanPasteTransferable(nsITransferable* aTransferable) override;
 
   /**
-   * OnInputLineBreak() is called when user inputs a line break with
+   * InsertLineBreakAsAction() is called when user inputs a line break with
    * Shift + Enter or something.
    */
-  nsresult OnInputLineBreak();
+  virtual nsresult InsertLineBreakAsAction() override;
+
+  /**
+   * InsertParagraphSeparatorAsAction() is called when user tries to separate
+   * current paragraph with Enter key press in HTMLEditor or something.
+   */
+  nsresult InsertParagraphSeparatorAsAction();
 
   /**
    * CreateElementWithDefaults() creates new element whose name is
    * aTagName with some default attributes are set.  Note that this is a
    * public utility method.  I.e., just creates element, not insert it
    * into the DOM tree.
    * NOTE: This is available for internal use too since this does not change
    *       the DOM tree nor undo transactions, and does not refer Selection,
@@ -993,16 +1001,22 @@ protected: // Called by helper classes.
   virtual void
   OnStartToHandleTopLevelEditSubAction(
     EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
   virtual void OnEndHandlingTopLevelEditSubAction() override;
 
 protected: // Shouldn't be used by friend classes
   virtual ~HTMLEditor();
 
+  /**
+   * InsertParagraphSeparatorAsSubAction() inserts a line break if it's
+   * HTMLEditor and it's possible.
+   */
+  nsresult InsertParagraphSeparatorAsSubAction();
+
   virtual nsresult SelectAllInternal() override;
 
   /**
    * SelectContentInternal() sets Selection to aContentToSelect to
    * aContentToSelect + 1 in parent of aContentToSelect.
    *
    * @param aContentToSelect    The content which should be selected.
    */
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -47,21 +47,25 @@ using namespace dom;
 template CreateElementResult
 TextEditRules::CreateBRInternal(const EditorDOMPoint& aPointToInsert,
                                 bool aCreateMozBR);
 template CreateElementResult
 TextEditRules::CreateBRInternal(const EditorRawDOMPoint& aPointToInsert,
                                 bool aCreateMozBR);
 
 #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
-  if (IsReadonly() || IsDisabled()) \
-  {                     \
+  if (IsReadonly() || IsDisabled()) {\
     *aCancel = true; \
-    return NS_OK;       \
-  };
+    return NS_OK; \
+  }
+
+#define CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED \
+  if (IsReadonly() || IsDisabled()) { \
+    return EditActionCanceled(NS_OK); \
+  }
 
 /********************************************************
  * mozilla::TextEditRules
  ********************************************************/
 
 TextEditRules::TextEditRules()
   : mTextEditor(nullptr)
   , mData(nullptr)
@@ -313,19 +317,27 @@ TextEditRules::WillDoAction(EditSubActio
 
   *aCancel = false;
   *aHandled = false;
 
   AutoSafeEditorData setData(*this, *mTextEditor);
 
   // my kingdom for dynamic cast
   switch (aInfo.mEditSubAction) {
-    case EditSubAction::eInsertParagraphSeparator:
+    case EditSubAction::eInsertLineBreak: {
       UndefineCaretBidiLevel();
-      return WillInsertBreak(aCancel, aHandled, aInfo.maxLength);
+      EditActionResult result = WillInsertLineBreak(aInfo.maxLength);
+      if (NS_WARN_IF(result.Failed())) {
+        return result.Rv();
+      }
+      *aCancel = result.Canceled();
+      *aHandled = result.Handled();
+      MOZ_ASSERT(!result.Ignored());
+      return NS_OK;
+    }
     case EditSubAction::eInsertText:
     case EditSubAction::eInsertTextComingFromIME:
       UndefineCaretBidiLevel();
       return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
                             aInfo.inString, aInfo.outString,
                             aInfo.maxLength);
     case EditSubAction::eSetText:
       UndefineCaretBidiLevel();
@@ -419,66 +431,115 @@ TextEditRules::WillInsert(bool* aCancel)
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     "Failed to remove the bogus node");
   mBogusNode = nullptr;
   return NS_OK;
 }
 
-nsresult
-TextEditRules::WillInsertBreak(bool* aCancel,
-                               bool* aHandled,
-                               int32_t aMaxLength)
+EditActionResult
+TextEditRules::WillInsertLineBreak(int32_t aMaxLength)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
-  if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
-    return NS_ERROR_INVALID_ARG;
+  MOZ_ASSERT(!IsSingleLineEditor());
+
+  CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
+
+  // handle docs with a max length
+  // NOTE, this function copies inString into outString for us.
+  NS_NAMED_LITERAL_STRING(inString, "\n");
+  nsAutoString outString;
+  bool didTruncate;
+  nsresult rv =
+    TruncateInsertionIfNeeded(&inString.AsString(),
+                              &outString, aMaxLength, &didTruncate);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return EditActionIgnored(rv);
   }
-  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
-  *aHandled = false;
-  if (IsSingleLineEditor()) {
-    *aCancel = true;
-  } else {
-    // handle docs with a max length
-    // NOTE, this function copies inString into outString for us.
-    NS_NAMED_LITERAL_STRING(inString, "\n");
-    nsAutoString outString;
-    bool didTruncate;
-    nsresult rv =
-      TruncateInsertionIfNeeded(&inString.AsString(),
-                                &outString, aMaxLength, &didTruncate);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+  if (didTruncate) {
+    return EditActionCanceled();
+  }
+
+  // if the selection isn't collapsed, delete it.
+  if (!SelectionRefPtr()->IsCollapsed()) {
+    rv = TextEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
+                                                    nsIEditor::eStrip);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
-    if (didTruncate) {
-      *aCancel = true;
-      return NS_OK;
-    }
-
-    *aCancel = false;
-
-    // if the selection isn't collapsed, delete it.
-    if (!SelectionRefPtr()->IsCollapsed()) {
-      rv = TextEditorRef().DeleteSelectionAsSubAction(nsIEditor::eNone,
-                                                      nsIEditor::eStrip);
-      if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    rv = WillInsert();
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      return EditActionIgnored(rv);
     }
   }
-  return NS_OK;
+
+  rv = WillInsert();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return EditActionIgnored(rv);
+  }
+
+  // get the (collapsed) selection location
+  nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
+  if (NS_WARN_IF(!firstRange)) {
+    return EditActionIgnored(NS_ERROR_FAILURE);
+  }
+
+  EditorRawDOMPoint pointToInsert(firstRange->StartRef());
+  if (NS_WARN_IF(!pointToInsert.IsSet())) {
+    return EditActionIgnored(NS_ERROR_FAILURE);
+  }
+  MOZ_ASSERT(pointToInsert.IsSetAndValid());
+
+  // Don't put text in places that can't have it.
+  if (!pointToInsert.IsInTextNode() &&
+      !TextEditorRef().CanContainTag(*pointToInsert.GetContainer(),
+                                     *nsGkAtoms::textTagName)) {
+    return EditActionIgnored(NS_ERROR_FAILURE);
+  }
+
+  nsCOMPtr<nsIDocument> doc = TextEditorRef().GetDocument();
+  if (NS_WARN_IF(!doc)) {
+    return EditActionIgnored(NS_ERROR_NOT_INITIALIZED);
+  }
+
+  // Don't change my selection in sub-transactions.
+  AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
+
+  // Insert a linefeed character.
+  EditorRawDOMPoint pointAfterInsertedLineBreak;
+  rv = TextEditorRef().InsertTextWithTransaction(
+                         *doc, NS_LITERAL_STRING("\n"), pointToInsert,
+                         &pointAfterInsertedLineBreak);
+  if (NS_WARN_IF(!pointAfterInsertedLineBreak.IsSet())) {
+    return EditActionIgnored(NS_ERROR_FAILURE);
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return EditActionIgnored(rv);
+  }
+
+  // set the selection to the correct location
+  MOZ_ASSERT(!pointAfterInsertedLineBreak.GetChild(),
+    "After inserting text into a text node, pointAfterInsertedLineBreak."
+    "GetChild() should be nullptr");
+  rv = SelectionRefPtr()->Collapse(pointAfterInsertedLineBreak);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return EditActionIgnored(rv);
+  }
+
+  // see if we're at the end of the editor range
+  EditorRawDOMPoint endPoint(EditorBase::GetEndPoint(*SelectionRefPtr()));
+  if (endPoint == pointAfterInsertedLineBreak) {
+    // SetInterlinePosition(true) means we want the caret to stick to the
+    // content on the "right".  We want the caret to stick to whatever is
+    // past the break.  This is because the break is on the same line we
+    // were on, but the next content will be on the following line.
+    SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
+  }
+
+  return EditActionHandled();
 }
 
 nsresult
 TextEditRules::CollapseSelectionToTrailingBRIfNeeded()
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // we only need to execute the stuff below if we are a plaintext editor.
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -180,23 +180,21 @@ protected:
                  const nsAString* inString, nsAString* outString,
                  int32_t aMaxLength);
 
   /**
    * Called before inserting a line break into the editor.
    * This method removes selected text if selection isn't collapsed.
    * Therefore, this might cause destroying the editor.
    *
-   * @param aCancel             Returns true if the operation is canceled.
-   * @param aHandled            Returns true if the edit action is handled.
    * @param aMaxLength          The maximum string length which the editor
    *                            allows to set.
    */
-  MOZ_MUST_USE nsresult
-  WillInsertBreak(bool* aCancel, bool* aHandled, int32_t aMaxLength);
+  MOZ_CAN_RUN_SCRIPT
+  MOZ_MUST_USE EditActionResult WillInsertLineBreak(int32_t aMaxLength);
 
   /**
    * Called before setting text to the text editor.
    * This method may actually set text to it.  Therefore, this might cause
    * destroying the text editor.
    *
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -409,17 +409,17 @@ TextEditor::HandleKeyPressEvent(WidgetKe
       aKeyboardEvent->PreventDefault();
       return OnInputText(NS_LITERAL_STRING("\t"));
     }
     case NS_VK_RETURN:
       if (IsSingleLineEditor() || !aKeyboardEvent->IsInputtingLineBreak()) {
         return NS_OK;
       }
       aKeyboardEvent->PreventDefault();
-      return OnInputParagraphSeparator();
+      return InsertLineBreakAsAction();
   }
 
   if (!aKeyboardEvent->IsInputtingText()) {
     // we don't PreventDefault() here or keybindings like control-x won't work
     return NS_OK;
   }
   aKeyboardEvent->PreventDefault();
   nsAutoString str(aKeyboardEvent->mCharCode);
@@ -438,27 +438,27 @@ TextEditor::OnInputText(const nsAString&
   nsresult rv = InsertTextAsSubAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
-TextEditor::OnInputParagraphSeparator()
+TextEditor::InsertLineBreakAsAction()
 {
-  AutoEditActionDataSetter editActionData(
-                             *this,
-                             EditAction::eInsertParagraphSeparator);
+  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
+  // XXX This may be called by execCommand() with "insertParagraph".
+  //     In such case, naming the transaction "TypingTxnName" is odd.
   AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
-  nsresult rv = InsertParagraphSeparatorAsAction();
+  nsresult rv = InsertLineBreakAsSubAction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 template<typename PT, typename CT>
 already_AddRefed<Element>
@@ -1065,119 +1065,66 @@ TextEditor::InsertTextAsSubAction(const 
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextEditor::InsertLineBreak()
 {
-  AutoEditActionDataSetter editActionData(
-                             *this,
-                             EditAction::eInsertParagraphSeparator);
+  if (NS_WARN_IF(IsSingleLineEditor())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoEditActionDataSetter editActionData(*this, EditAction::eInsertLineBreak);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  return InsertParagraphSeparatorAsAction();
+  AutoPlaceholderBatch treatAsOneTransaction(*this);
+  nsresult rv = InsertLineBreakAsSubAction();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 nsresult
-TextEditor::InsertParagraphSeparatorAsAction()
+TextEditor::InsertLineBreakAsSubAction()
 {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this,
-                                      EditSubAction::eInsertParagraphSeparator,
+                                      EditSubAction::eInsertLineBreak,
                                       nsIEditor::eNext);
 
-  EditSubActionInfo subActionInfo(EditSubAction::eInsertParagraphSeparator);
+  EditSubActionInfo subActionInfo(EditSubAction::eInsertLineBreak);
   subActionInfo.maxLength = mMaxTextLength;
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
+  if (cancel) {
+    return rv; // We don't need to call DidDoAction() if canceled.
+  }
+  // XXX DidDoAction() does nothing for eInsertParagraphSeparator.  However,
+  //     we should call it until we keep using this style.  Perhaps, each
+  //     editor method should call necessary method of
+  //     TextEditRules/HTMLEditRules directly.
+  rv = rules->DidDoAction(subActionInfo, rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    // XXX DidDoAction() won't be called when WillDoAction() returns error.
-    //     Perhaps, we should move the code between WillDoAction() and
-    //     DidDoAction() to a new method and guarantee that DidDoAction() is
-    //     always called after WillDoAction().
     return rv;
   }
-
-  if (!cancel && !handled) {
-    // get the (collapsed) selection location
-    nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
-    if (NS_WARN_IF(!firstRange)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    EditorRawDOMPoint pointToInsert(firstRange->StartRef());
-    if (NS_WARN_IF(!pointToInsert.IsSet())) {
-      return NS_ERROR_FAILURE;
-    }
-    MOZ_ASSERT(pointToInsert.IsSetAndValid());
-
-    // don't put text in places that can't have it
-    if (!pointToInsert.IsInTextNode() &&
-        !CanContainTag(*pointToInsert.GetContainer(),
-                       *nsGkAtoms::textTagName)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // we need to get the doc
-    nsCOMPtr<nsIDocument> doc = GetDocument();
-    if (NS_WARN_IF(!doc)) {
-      return NS_ERROR_NOT_INITIALIZED;
-    }
-
-    // don't change my selection in subtransactions
-    AutoTransactionsConserveSelection dontChangeMySelection(*this);
-
-    // insert a linefeed character
-    EditorRawDOMPoint pointAfterInsertedLineBreak;
-    rv = InsertTextWithTransaction(*doc, NS_LITERAL_STRING("\n"), pointToInsert,
-                                   &pointAfterInsertedLineBreak);
-    if (NS_WARN_IF(!pointAfterInsertedLineBreak.IsSet())) {
-      rv = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
-    }
-    if (NS_SUCCEEDED(rv)) {
-      // set the selection to the correct location
-      MOZ_ASSERT(!pointAfterInsertedLineBreak.GetChild(),
-        "After inserting text into a text node, pointAfterInsertedLineBreak."
-        "GetChild() should be nullptr");
-      rv = SelectionRefPtr()->Collapse(pointAfterInsertedLineBreak);
-      if (NS_SUCCEEDED(rv)) {
-        // see if we're at the end of the editor range
-        EditorRawDOMPoint endPoint(EditorBase::GetEndPoint(*SelectionRefPtr()));
-        if (endPoint == pointAfterInsertedLineBreak) {
-          // SetInterlinePosition(true) means we want the caret to stick to the
-          // content on the "right".  We want the caret to stick to whatever is
-          // past the break.  This is because the break is on the same line we
-          // were on, but the next content will be on the following line.
-          SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
-        }
-      }
-    }
-  }
-
-  if (!cancel) {
-    // post-process, always called if WillInsertBreak didn't return cancel==true
-    rv = rules->DidDoAction(subActionInfo, rv);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
-                         "TextEditRules::DidDoAction() failed");
-  }
-  return rv;
+  return NS_OK;
 }
 
 nsresult
 TextEditor::SetText(const nsAString& aString)
 {
   MOZ_ASSERT(aString.FindChar(static_cast<char16_t>('\r')) == kNotFound);
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eSetText);
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -182,20 +182,20 @@ public:
    * @param aString             The string to set.
    * @param aReplaceRange       The range to be replaced.
    *                            If nullptr, all contents will be replaced.
    */
   nsresult ReplaceTextAsAction(const nsAString& aString,
                                nsRange* aReplaceRange = nullptr);
 
   /**
-   * OnInputParagraphSeparator() is called when user tries to separate current
-   * paragraph with Enter key press or something.
+   * InsertLineBreakAsAction() is called when user inputs a line break with
+   * Enter or something.
    */
-  nsresult OnInputParagraphSeparator();
+  virtual nsresult InsertLineBreakAsAction();
 
   /**
    * OnCompositionStart() is called when editor receives eCompositionStart
    * event which should be handled in this editor.
    */
   nsresult OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent);
 
   /**
@@ -371,23 +371,20 @@ protected: // Shouldn't be used by frien
   /**
    * OnInputText() is called when user inputs text with keyboard or something.
    *
    * @param aStringToInsert     The string to insert.
    */
   nsresult OnInputText(const nsAString& aStringToInsert);
 
   /**
-   * InsertParagraphSeparatorAsAction() inserts a line break if it's TextEditor
-   * or inserts new paragraph if it's HTMLEditor and it's possible.
-   * Although, this method is implementation of
-   * nsIPlaintextEditor.insertLineBreak(), this treats the input is an edit
-   * action.
+   * InsertLineBreakAsSubAction() inserts a line break, i.e., \n if it's
+   * TextEditor or <br> if it's HTMLEditor.
    */
-  nsresult InsertParagraphSeparatorAsAction();
+  nsresult InsertLineBreakAsSubAction();
 
   nsresult InsertTextAt(const nsAString& aStringToInsert,
                         nsINode* aDestinationNode,
                         int32_t aDestOffset,
                         bool aDoDeleteSelection);
 
   virtual nsresult InsertFromDataTransfer(dom::DataTransfer* aDataTransfer,
                                           int32_t aIndex,
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -1482,17 +1482,24 @@ ReadableStreamErrorInternal(JSContext* c
 
     // Step 2: Assert: stream.[[state]] is "readable".
     MOZ_ASSERT(stream->readable());
 
     // Step 3: Set stream.[[state]] to "errored".
     stream->setErrored();
 
     // Step 4: Set stream.[[storedError]] to e.
-    stream->setStoredError(e);
+    {
+        AutoRealm ar(cx, stream);
+        RootedValue wrappedError(cx, e);
+        if (!cx->compartment()->wrap(cx, &wrappedError)) {
+            return false;
+        }
+        stream->setStoredError(wrappedError);
+    }
 
     // Step 6: If reader is undefined, return (reordered).
     if (!stream->hasReader()) {
         return true;
     }
 
     // Step 5: Let reader be stream.[[reader]].
     Rooted<ReadableStreamReader*> reader(cx);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/ReadableStream/bug-1501502.js
@@ -0,0 +1,24 @@
+// A stream can become errored with an exception from another realm.
+
+let g = newGlobal();
+let g_enqueue;
+new g.ReadableStream({
+    start(controller) {
+        g_enqueue = controller.enqueue;
+    },
+});
+
+let controller;
+let stream = new ReadableStream({
+    start(c) {
+        controller = c;
+    }
+}, {
+    size(chunk) {}
+});
+
+assertThrowsInstanceOf(() => g_enqueue.call(controller, {}), g.RangeError);
+
+if (typeof reportCompare == 'function')
+    reportCompare(0, 0);
+
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -961,20 +961,20 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   if (originalURI != uri) {
     rv = nsContentUtils::GetSecurityManager()->
       CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI,
                                 nsIScriptSecurityManager::STANDARD);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!mHasBeenCrossSite &&
-      NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, true, false)) &&
+      NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, false, false)) &&
       (originalURI == uri ||
        NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI,
-                                                       true, false)))) {
+                                                       false, false)))) {
     return NS_OK;
   }
 
   // if the CSP directive 'upgrade-insecure-requests' is used then we should
   // not incorrectly require CORS if the only difference of a subresource
   // request and the main page is the scheme.
   // e.g. toplevel page: https://www.example.com loads
   //                xhr: http://www.example.com/somefoo,
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -4,19 +4,39 @@ Change log
 All notable changes to this program is documented in this file.
 
 
 Unreleased
 ----------
 
 ### Added
 
+- Introduces `strictFileInteractability` capability
+
+  The new capabilitiy indicates if strict interactability checks
+  should be applied to `<input type=file>` elements.  As strict
+  interactability checks are off by default, there is a change
+  in behaviour when using [Element Send Keys] with hidden file
+  upload controls.
+
 - Added new endpoint `GET /session/{session id}/moz/screenshot/full`
   for taking full document screenshots, thanks to Greg Fraley.
 
+# Changed
+
+- Allow file uploads to hidden `<input type=file>` elements
+
+  Through a series of changes to the WebDriver specification,
+  geckodriver is now aligned with chromedriver’s behaviour that
+  allows interaction with hidden `<input type=file>` elements.
+
+  This allows WebDriver to be used with various popular web frameworks
+  that—through indirection—hides the file upload control and
+  invokes it through other means.
+
 
 0.23.0 (2018-10-03)
 -------------------
 
 This release contains a number of fixes for regressions introduced
 in 0.22.0, where we shipped a significant refactoring to the way
 geckodriver internally dealt with JSON serialisation.
 
--- a/testing/geckodriver/src/capabilities.rs
+++ b/testing/geckodriver/src/capabilities.rs
@@ -151,16 +151,20 @@ impl<'a> BrowserCapabilities for Firefox
         version: &str,
         comparison: &str,
     ) -> WebDriverResult<bool> {
         try!(Version::from_str(version).or_else(|x| Err(convert_version_error(x))))
             .matches(comparison)
             .or_else(|x| Err(convert_version_error(x)))
     }
 
+    fn strict_file_interactability(&mut self, _: &Capabilities) -> WebDriverResult<bool> {
+        Ok(true)
+    }
+
     fn accept_proxy(&mut self, _: &Capabilities, _: &Capabilities) -> WebDriverResult<bool> {
         Ok(true)
     }
 
     fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()> {
         if !name.starts_with("moz:") {
             return Ok(());
         }
--- a/testing/marionette/capabilities.js
+++ b/testing/marionette/capabilities.js
@@ -392,16 +392,17 @@ class Capabilities extends Map {
       ["browserVersion", appinfo.version],
       ["platformName", getWebDriverPlatformName()],
       ["platformVersion", Services.sysinfo.getProperty("version")],
       ["acceptInsecureCerts", false],
       ["pageLoadStrategy", PageLoadStrategy.Normal],
       ["proxy", new Proxy()],
       ["setWindowRect", appinfo.name == "firefox"],
       ["timeouts", new Timeouts()],
+      ["strictFileInteractability", false],
       ["unhandledPromptBehavior", UnhandledPromptBehavior.DismissAndNotify],
 
       // features
       ["rotatable", appinfo.name == "B2G"],
 
       // proprietary
       ["moz:accessibilityChecks", false],
       ["moz:headless", Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless],
@@ -488,16 +489,20 @@ class Capabilities extends Map {
             throw new InvalidArgumentError("setWindowRect is only supported in Firefox desktop");
           }
           break;
 
         case "timeouts":
           v = Timeouts.fromJSON(v);
           break;
 
+        case "strictFileInteractability":
+          v = assert.boolean(v);
+          break;
+
         case "unhandledPromptBehavior":
           assert.string(v, pprint`Expected ${k} to be a string, got ${v}`);
           if (!Object.values(UnhandledPromptBehavior).includes(v)) {
             throw new InvalidArgumentError(
                 `Unknown unhandled prompt behavior: ${v}`);
           }
           break;
 
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1249,17 +1249,17 @@ class Marionette(object):
             accept a WebDriver conforming capabilities dictionary
             (including alwaysMatch, firstMatch, desiredCapabilities,
             or requriedCapabilities), and only recognises extension
             capabilities that are specific to Marionette.
         :param timeout: Optional timeout in seconds for the server to be ready.
         :returns: A dictionary of the capabilities offered.
         """
         if capabilities is None:
-            capabilities = {}
+            capabilities = {"strictFileInteractability": True}
 
         if timeout is None:
             timeout = self.startup_timeout
 
         self.crashed = 0
 
         if self.instance:
             returncode = self.instance.runner.returncode
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2528,36 +2528,37 @@ GeckoDriver.prototype.getElementRect = a
  * Send key presses to element after focusing on it.
  *
  * @param {string} id
  *     Reference ID to the element that will be checked.
  * @param {string} text
  *     Value to send to the element.
  *
  * @throws {InvalidArgumentError}
- *     If <var>id</var> or <var>text</var> are not strings.
+ *     If `id` or `text` are not strings.
  * @throws {NoSuchElementError}
- *     If element represented by reference <var>id</var> is unknown.
+ *     If element represented by reference `id` is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.sendKeysToElement = async function(cmd) {
   assert.open(this.getCurrentWindow());
   await this._handleUserPrompts();
 
   let id = assert.string(cmd.parameters.id);
   let text = assert.string(cmd.parameters.text);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
-      await interaction.sendKeysToElement(el, text, this.a11yChecks);
+      await interaction.sendKeysToElement(el, text,
+          {accessibilityChecks: this.a11yChecks});
       break;
 
     case Context.Content:
       await this.listener.sendKeysToElement(webEl, text);
       break;
 
     default:
       throw new TypeError(`Unknown context: ${this.context}`);
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
@@ -57,30 +57,32 @@ class TestCapabilities(MarionetteTestCas
     def test_mandated_capabilities(self):
         self.assertIn("browserName", self.caps)
         self.assertIn("browserVersion", self.caps)
         self.assertIn("platformName", self.caps)
         self.assertIn("platformVersion", self.caps)
         self.assertIn("acceptInsecureCerts", self.caps)
         self.assertIn("setWindowRect", self.caps)
         self.assertIn("timeouts", self.caps)
+        self.assertIn("strictFileInteractability", self.caps)
 
         self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower())
         self.assertEqual(self.caps["browserVersion"], self.appinfo["version"])
         self.assertEqual(self.caps["platformName"], self.os_name)
         self.assertEqual(self.caps["platformVersion"], self.os_version)
         self.assertFalse(self.caps["acceptInsecureCerts"])
         if self.appinfo["name"] == "Firefox":
             self.assertTrue(self.caps["setWindowRect"])
         else:
             self.assertFalse(self.caps["setWindowRect"])
         self.assertDictEqual(self.caps["timeouts"],
                              {"implicit": 0,
                               "pageLoad": 300000,
                               "script": 30000})
+        self.assertTrue(self.caps["strictFileInteractability"])
 
     def test_supported_features(self):
         self.assertIn("rotatable", self.caps)
 
     def test_additional_capabilities(self):
         self.assertIn("moz:processID", self.caps)
         self.assertEqual(self.caps["moz:processID"], self.appinfo["processID"])
         self.assertEqual(self.marionette.process_id, self.appinfo["processID"])
@@ -175,16 +177,34 @@ class TestCapabilityMatching(MarionetteT
         self.delete_session()
 
         timeouts = {"implicit": 0, "pageLoad": 2.0, "script": 2**53 - 1}
         self.marionette.start_session({"timeouts": timeouts})
         self.assertIn("timeouts", self.marionette.session_capabilities)
         self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts)
         self.assertDictEqual(self.marionette._send_message("WebDriver:GetTimeouts"), timeouts)
 
+    def test_strict_file_interactability(self):
+        for value in ["", 2.5, {}, []]:
+            print("  type {}".format(type(value)))
+            with self.assertRaises(SessionNotCreatedException):
+                self.marionette.start_session({"strictFileInteractability": value})
+
+        self.delete_session()
+
+        self.marionette.start_session({"strictFileInteractability": True})
+        self.assertIn("strictFileInteractability", self.marionette.session_capabilities)
+        self.assertTrue(self.marionette.session_capabilities["strictFileInteractability"])
+
+        self.delete_session()
+
+        self.marionette.start_session({"strictFileInteractability": False})
+        self.assertIn("strictFileInteractability", self.marionette.session_capabilities)
+        self.assertFalse(self.marionette.session_capabilities["strictFileInteractability"])
+
     def test_unhandled_prompt_behavior(self):
         behaviors = [
             "accept",
             "accept and notify",
             "dismiss",
             "dismiss and notify",
             "ignore"
         ]
--- a/testing/marionette/interaction.js
+++ b/testing/marionette/interaction.js
@@ -535,41 +535,51 @@ interaction.setFormControlValue = functi
 
 /**
  * Send keys to element.
  *
  * @param {DOMElement|XULElement} el
  *     Element to send key events to.
  * @param {Array.<string>} value
  *     Sequence of keystrokes to send to the element.
- * @param {boolean=} [strict=false] strict
+ * @param {boolean=} strictFileInteractability
+ *     Run interactability checks on `<input type=file>` elements.
+ * @param {boolean=} accessibilityChecks
  *     Enforce strict accessibility tests.
- * @param {boolean=} [specCompat=false] specCompat
+ * @param {boolean=} webdriverClick
  *     Use WebDriver specification compatible interactability definition.
  */
-interaction.sendKeysToElement = async function(
-    el, value, strict = false, specCompat = false) {
-  const a11y = accessibility.get(strict);
+interaction.sendKeysToElement = async function(el, value,
+    {
+      strictFileInteractability = false,
+      accessibilityChecks = false,
+      webdriverClick = false,
+    } = {}) {
+  const a11y = accessibility.get(accessibilityChecks);
 
-  if (specCompat) {
-    await webdriverSendKeysToElement(el, value, a11y);
+  if (webdriverClick) {
+    await webdriverSendKeysToElement(
+        el, value, a11y, strictFileInteractability);
   } else {
     await legacySendKeysToElement(el, value, a11y);
   }
 };
 
-async function webdriverSendKeysToElement(el, value, a11y) {
+async function webdriverSendKeysToElement(el, value,
+    a11y, strictFileInteractability) {
   const win = getWindow(el);
 
-  let containerEl = element.getContainer(el);
+  if (el.type != "file" || strictFileInteractability) {
+    let containerEl = element.getContainer(el);
 
-  // TODO: Wait for element to be keyboard-interactible
-  if (!interaction.isKeyboardInteractable(containerEl)) {
-    throw new ElementNotInteractableError(
-        pprint`Element ${el} is not reachable by keyboard`);
+    // TODO: Wait for element to be keyboard-interactible
+    if (!interaction.isKeyboardInteractable(containerEl)) {
+      throw new ElementNotInteractableError(
+          pprint`Element ${el} is not reachable by keyboard`);
+    }
   }
 
   let acc = await a11y.getAccessible(el, true);
   a11y.assertActionable(acc, el);
 
   el.focus();
   interaction.moveCaretToEnd(el);
 
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -1299,21 +1299,22 @@ function isElementEnabled(el) {
  * and Radio Button states, or option elements.
  */
 function isElementSelected(el) {
   return interaction.isElementSelected(
       el, capabilities.get("moz:accessibilityChecks"));
 }
 
 async function sendKeysToElement(el, val) {
-  await interaction.sendKeysToElement(
-      el, val,
-      capabilities.get("moz:accessibilityChecks"),
-      capabilities.get("moz:webdriverClick"),
-  );
+  let opts = {
+    strictFileInteractability: capabilities.get("strictFileInteractability"),
+    accessibilityChecks: capabilities.get("moz:accessibilityChecks"),
+    webdriverClick: capabilities.get("moz:webdriverClick"),
+  };
+  await interaction.sendKeysToElement(el, val, opts);
 }
 
 /** Clear the text of an element. */
 function clearElement(el) {
   interaction.clearElement(el);
 }
 
 /** Switch the current context to the specified host's Shadow DOM. */
--- a/testing/marionette/test/unit/test_capabilities.js
+++ b/testing/marionette/test/unit/test_capabilities.js
@@ -377,16 +377,17 @@ add_test(function test_Capabilities_ctor
   ok(caps.has("platformName"));
   ok(["linux", "mac", "windows", "android"].includes(caps.get("platformName")));
   ok(caps.has("platformVersion"));
   equal(PageLoadStrategy.Normal, caps.get("pageLoadStrategy"));
   equal(false, caps.get("acceptInsecureCerts"));
   ok(caps.get("timeouts") instanceof Timeouts);
   ok(caps.get("proxy") instanceof Proxy);
   equal(caps.get("setWindowRect"), false); // xpcshell does not populate appinfo
+  equal(caps.get("strictFileInteractability"), false);
 
   ok(caps.has("rotatable"));
 
   equal(false, caps.get("moz:accessibilityChecks"));
   ok(caps.has("moz:processID"));
   ok(caps.has("moz:profile"));
   equal(false, caps.get("moz:useNonSpecCompliantPointerOrigin"));
   equal(true, caps.get("moz:webdriverClick"));
@@ -408,16 +409,17 @@ add_test(function test_Capabilities_toJS
   equal(caps.get("browserVersion"), json.browserVersion);
   equal(caps.get("platformName"), json.platformName);
   equal(caps.get("platformVersion"), json.platformVersion);
   equal(caps.get("pageLoadStrategy"), json.pageLoadStrategy);
   equal(caps.get("acceptInsecureCerts"), json.acceptInsecureCerts);
   deepEqual(caps.get("timeouts").toJSON(), json.timeouts);
   equal(undefined, json.proxy);
   equal(caps.get("setWindowRect"), json.setWindowRect);
+  equal(caps.get("strictFileInteractability"), json.strictFileInteractability);
 
   equal(caps.get("rotatable"), json.rotatable);
 
   equal(caps.get("moz:accessibilityChecks"), json["moz:accessibilityChecks"]);
   equal(caps.get("moz:processID"), json["moz:processID"]);
   equal(caps.get("moz:profile"), json["moz:profile"]);
   equal(caps.get("moz:useNonSpecCompliantPointerOrigin"),
       json["moz:useNonSpecCompliantPointerOrigin"]);
@@ -460,16 +462,21 @@ add_test(function test_Capabilities_from
   let timeoutsConfig = {implicit: 123};
   caps = fromJSON({timeouts: timeoutsConfig});
   equal(123, caps.get("timeouts").implicit);
 
   caps = fromJSON({setWindowRect: false});
   equal(false, caps.get("setWindowRect"));
   Assert.throws(() => fromJSON({setWindowRect: true}), InvalidArgumentError);
 
+  caps = fromJSON({strictFileInteractability: false});
+  equal(false, caps.get("strictFileInteractability"));
+  caps = fromJSON({strictFileInteractability: true});
+  equal(true, caps.get("strictFileInteractability"));
+
   caps = fromJSON({"moz:accessibilityChecks": true});
   equal(true, caps.get("moz:accessibilityChecks"));
   caps = fromJSON({"moz:accessibilityChecks": false});
   equal(false, caps.get("moz:accessibilityChecks"));
   Assert.throws(() => fromJSON({"moz:accessibilityChecks": "foo"}), InvalidArgumentError);
   Assert.throws(() => fromJSON({"moz:accessibilityChecks": 1}), InvalidArgumentError);
 
   caps = fromJSON({"moz:useNonSpecCompliantPointerOrigin": false});
--- a/testing/web-platform/tests/webdriver/tests/element_send_keys/file_upload.py
+++ b/testing/web-platform/tests/webdriver/tests/element_send_keys/file_upload.py
@@ -1,8 +1,10 @@
+import pytest
+
 from tests.support.asserts import assert_error, assert_files_uploaded, assert_success
 from tests.support.inline import inline
 
 from . import map_files_to_multiline_text
 
 
 def element_send_keys(session, element, text):
     return session.transport.send(
@@ -135,8 +137,92 @@ def test_single_file_appends_with_multip
 
     response = element_send_keys(session, element, str(files[0]))
     assert_success(response)
 
     response = element_send_keys(session, element, str(files[1]))
     assert_success(response)
 
     assert_files_uploaded(session, element, files)
+
+
+def test_transparent(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("""<input type=file style="opacity: 0">""")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_success(response)
+    assert_files_uploaded(session, element, files)
+
+
+def test_obscured(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("""
+        <style>
+          div {
+            position: absolute;
+            width: 100vh;
+            height: 100vh;
+            background: blue;
+            top: 0;
+            left: 0;
+          }
+        </style>
+
+        <input type=file>
+        <div></div>
+        """)
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_success(response)
+    assert_files_uploaded(session, element, files)
+
+
+def test_outside_viewport(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("""<input type=file style="margin-left: -100vh">""")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_success(response)
+    assert_files_uploaded(session, element, files)
+
+
+def test_hidden(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("<input type=file hidden>")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_success(response)
+    assert_files_uploaded(session, element, files)
+
+
+def test_display_none(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("""<input type=file style="display: none">""")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_success(response)
+    assert_files_uploaded(session, element, files)
+
+
+@pytest.mark.capabilities({"strictFileInteractability": True})
+def test_strict_hidden(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("<input type=file hidden>")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_error(response, "element not interactable")
+
+
+@pytest.mark.capabilities({"strictFileInteractability": True})
+def test_strict_display_none(session, create_files):
+    files = create_files(["foo"])
+    session.url = inline("""<input type=file style="display: none">""")
+    element = session.find.css("input", all=False)
+
+    response = element_send_keys(session, element, str(files[0]))
+    assert_error(response, "element not interactable")
--- a/testing/web-platform/tests/webdriver/tests/new_session/response.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/response.py
@@ -16,28 +16,30 @@ def test_sessionid(new_session, add_brow
     ("browserName", basestring),
     ("browserVersion", basestring),
     ("platformName", basestring),
     ("acceptInsecureCerts", bool),
     ("pageLoadStrategy", basestring),
     ("proxy", dict),
     ("setWindowRect", bool),
     ("timeouts", dict),
+    ("strictFileInteractability", bool),
     ("unhandledPromptBehavior", basestring),
 ])
 def test_capability_type(session, capability, type):
     assert isinstance(session.capabilities, dict)
     assert capability in session.capabilities
     assert isinstance(session.capabilities[capability], type)
 
 
 @pytest.mark.parametrize("capability, default_value", [
     ("acceptInsecureCerts", False),
     ("pageLoadStrategy", "normal"),
     ("proxy", {}),
     ("setWindowRect", True),
     ("timeouts", {"implicit": 0, "pageLoad": 300000, "script": 30000}),
+    ("strictFileInteractability", False),
     ("unhandledPromptBehavior", "dismiss and notify"),
 ])
 def test_capability_default_value(session, capability, default_value):
     assert isinstance(session.capabilities, dict)
     assert capability in session.capabilities
     assert session.capabilities[capability] == default_value
--- a/testing/web-platform/tests/webdriver/tests/new_session/support/create.py
+++ b/testing/web-platform/tests/webdriver/tests/new_session/support/create.py
@@ -22,16 +22,19 @@ valid_data = [
         None,
     ]),
     ("timeouts", [
         None, {},
         {"script": 0, "pageLoad": 2.0, "implicit": 2**53 - 1},
         {"script": 50, "pageLoad": 25},
         {"script": 500},
     ]),
+    ("strictFileInteractability", [
+        True, False, None,
+    ]),
     ("unhandledPromptBehavior", [
         "dismiss",
         "accept",
         None,
     ]),
     ("test:extension", [
         None, False, "abc", 123, [],
         {"key": "value"},
@@ -90,41 +93,44 @@ invalid_data = [
         {"pageLoad": []},
         {"pageLoad": "10"},
         {"pageLoad": 2.5},
         {"pageLoad": -1},
         {"pageLoad": 2**53},
         {"pageLoad": {"value": 10}},
         {"pageLoad": 10, "invalid": 10},
     ]),
+    ("strictFileInteractability", [
+        1, [], {}, "false",
+    ]),
     ("unhandledPromptBehavior", [
         1, [], {}, False,
         "DISMISS",
         "dismissABC",
         "Accept",
         " dismiss",
         "dismiss ",
     ])
 ]
 
 invalid_extensions = [
+    "automaticInspection",
+    "automaticProfiling",
+    "browser",
+    "chromeOptions",
+    "ensureCleanSession",
     "firefox",
     "firefox_binary",
     "firefoxOptions",
-    "chromeOptions",
-    "automaticInspection",
-    "automaticProfiling",
-    "platform",
-    "version",
-    "browser",
-    "platformVersion",
+    "initialBrowserUrl",
     "javascriptEnabled",
-    "nativeEvents",
-    "seleniumProtocol",
-    "profile",
-    "trustAllSSLCertificates",
-    "initialBrowserUrl",
-    "requireWindowFocus",
     "logFile",
     "logLevel",
+    "nativeEvents",
+    "platform",
+    "platformVersion",
+    "profile",
+    "requireWindowFocus",
     "safari.options",
-    "ensureCleanSession",
+    "seleniumProtocol",
+    "trustAllSSLCertificates",
+    "version",
 ]
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -38,16 +38,19 @@ pub trait BrowserCapabilities {
 
     /// Whether insecure certificates are supported
     fn accept_insecure_certs(&mut self, &Capabilities) -> WebDriverResult<bool>;
 
     /// Indicates whether driver supports all of the window resizing and
     /// repositioning commands.
     fn set_window_rect(&mut self, &Capabilities) -> WebDriverResult<bool>;
 
+    /// Indicates that interactability checks will be applied to `<input type=file>`.
+    fn strict_file_interactability(&mut self, &Capabilities) -> WebDriverResult<bool>;
+
     fn accept_proxy(
         &mut self,
         proxy_settings: &Map<String, Value>,
         &Capabilities,
     ) -> WebDriverResult<bool>;
 
     /// Type check custom properties
     ///
@@ -118,17 +121,17 @@ impl SpecNewSessionParameters {
             .map(|(k, _)| k.clone())
             .collect::<Vec<String>>();
         for key in null_entries {
             capabilities.remove(&key);
         }
 
         for (key, value) in &capabilities {
             match &**key {
-                x @ "acceptInsecureCerts" | x @ "setWindowRect" => if !value.is_boolean() {
+                x @ "acceptInsecureCerts" | x @ "setWindowRect" | x @ "strictFileInteractability" => if !value.is_boolean() {
                     return Err(WebDriverError::new(
                         ErrorStatus::InvalidArgument,
                         format!("{} is not boolean: {}", x, value),
                     ));
                 },
                 x @ "browserName" | x @ "browserVersion" | x @ "platformName" => {
                     if !value.is_string() {
                         return Err(WebDriverError::new(
@@ -477,16 +480,25 @@ impl CapabilitiesMatching for SpecNewSes
                             if value.as_bool().unwrap_or(false)
                                 && !browser_capabilities
                                     .set_window_rect(merged)
                                     .unwrap_or(false)
                             {
                                 return None;
                             }
                         }
+                        "strictFileInteractability" => {
+                            if value.as_bool().unwrap_or(false)
+                                && !browser_capabilities
+                                    .strict_file_interactability(merged)
+                                    .unwrap_or(false)
+                            {
+                                return None;
+                            }
+                        }
                         "proxy" => {
                             let default = Map::new();
                             let proxy = value.as_object().unwrap_or(&default);
                             if !browser_capabilities
                                 .accept_proxy(&proxy, merged)
                                 .unwrap_or(false)
                             {
                                 return None;