Merge mozilla-central to mozilla-inbound. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Sat, 03 Nov 2018 23:47:49 +0200
changeset 500767 719fa010f362459aa154651d44d9b452a494f330
parent 500766 86ab0cd2855c06099abce0a68700127dcbd26193 (current diff)
parent 500765 cfdbecb2e2305dba0ccac931aa91423135039d2f (diff)
child 500768 5fb87d2312ea4881026ecaac6488fbdc2e4a7725
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)
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. CLOSED TREE
python/mozboot/mozboot/test/python.ini
python/mozboot/mozboot/test/test_write_config.py
--- 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/python/moz.build
+++ b/python/moz.build
@@ -50,17 +50,16 @@ SPHINX_TREES['mach'] = 'mach/docs'
 
 SPHINX_TREES['python'] = 'docs'
 
 with Files('mach/docs/**'):
     SCHEDULES.exclusive = ['docs']
 
 PYTHON_UNITTEST_MANIFESTS += [
     'mach/mach/test/python.ini',
-    'mozboot/mozboot/test/python.ini',
     'mozbuild/dumbmake/test/python.ini',
     'mozlint/test/python.ini',
     'mozrelease/test/python.ini',
     'mozterm/test/python.ini',
     'mozversioncontrol/test/python.ini',
 ]
 
 if CONFIG['MOZ_BUILD_APP']:
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -403,30 +403,16 @@ class BaseBootstrapper(object):
                 print("ERROR! Please enter a valid option!")
                 limit -= 1
 
         if limit > 0:
             return choice
         else:
             raise Exception("Error! Reached max attempts of entering option.")
 
-    def prompt_yesno(self, prompt):
-        ''' Prompts the user with prompt and requires a yes/no answer.'''
-        valid = False
-        while not valid:
-            choice = raw_input(prompt + ' [Y/n]: ').strip().lower()[:1]
-            if choice == '':
-                choice = 'y'
-            if choice not in ('y', 'n'):
-                print('ERROR! Please enter y or n!')
-            else:
-                valid = True
-
-        return choice == 'y'
-
     def _ensure_package_manager_updated(self):
         if self.package_manager_updated:
             return
 
         self._update_package_manager()
         self.package_manager_updated = True
 
     def _update_package_manager(self):
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -4,20 +4,16 @@
 
 # If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
 from __future__ import absolute_import, print_function
 
 import platform
 import sys
 import os
 import subprocess
-from six.moves.configparser import (
-    Error as ConfigParserError,
-    RawConfigParser,
-)
 
 # Don't forgot to add new mozboot modules to the bootstrap download
 # list in bin/bootstrap.py!
 from mozboot.base import MODERN_RUST_VERSION
 from mozboot.centosfedora import CentOSFedoraBootstrapper
 from mozboot.debian import DebianBootstrapper
 from mozboot.freebsd import FreeBSDBootstrapper
 from mozboot.gentoo import GentooBootstrapper
@@ -184,64 +180,27 @@ To add git-cinnabar to the PATH, edit yo
 may be called ~/.bashrc or ~/.bash_profile or ~/.profile, and add the following
 lines:
 
     export PATH="{}:$PATH"
 
 Then restart your shell.
 '''
 
-TELEMETRY_OPT_IN_PROMPT = '''
-Would you like to enable build system telemetry?
-
-Mozilla collects data about local builds in order to make builds faster and
-improve developer tooling. To learn more about the data we intend to collect
-read here:
-https://firefox-source-docs.mozilla.org/build/buildsystem/telemetry.html.
-
-If you have questions, please ask in #build in irc.mozilla.org. If you would
-like to opt out of data collection, select (N) at the prompt.
-
-Your choice'''
-
-
-def update_or_create_build_telemetry_config(path):
-    """Write a mach config file enabling build telemetry to `path`. If the file does not exist,
-    create it. If it exists, add the new setting to the existing data.
-
-    This is standalone from mach's `ConfigSettings` so we can use it during bootstrap
-    without a source checkout.
-    """
-    config = RawConfigParser()
-    if os.path.exists(path):
-        try:
-            config.read([path])
-        except ConfigParserError as e:
-            print('Your mach configuration file at `{path}` is not parseable:\n{error}'.format(
-                path=path, error=e))
-            return False
-    if not config.has_section('build'):
-        config.add_section('build')
-    config.set('build', 'telemetry', 'true')
-    with open(path, 'wb') as f:
-        config.write(f)
-    return True
-
 
 class Bootstrapper(object):
     """Main class that performs system bootstrap."""
 
     def __init__(self, finished=FINISHED, choice=None, no_interactive=False,
-                 hg_configure=False, no_system_changes=False, mach_context=None):
+                 hg_configure=False, no_system_changes=False):
         self.instance = None
         self.finished = finished
         self.choice = choice
         self.hg_configure = hg_configure
         self.no_system_changes = no_system_changes
-        self.mach_context = mach_context
         cls = None
         args = {'no_interactive': no_interactive,
                 'no_system_changes': no_system_changes}
 
         if sys.platform.startswith('linux'):
             distro, version, dist_id = platform.linux_distribution()
 
             if distro in ('CentOS', 'CentOS Linux', 'Fedora'):
@@ -372,30 +331,16 @@ class Bootstrapper(object):
         if not have_clone:
             print(STYLE_NODEJS_REQUIRES_CLONE)
             sys.exit(1)
 
         self.instance.state_dir = state_dir
         self.instance.ensure_stylo_packages(state_dir, checkout_root)
         self.instance.ensure_node_packages(state_dir, checkout_root)
 
-    def check_telemetry_opt_in(self, state_dir):
-        # We can't prompt the user.
-        if self.instance.no_interactive:
-            return
-        # Don't prompt if the user already has a setting for this value.
-        if self.mach_context is not None and 'telemetry' in self.mach_context.settings.build:
-            return
-        choice = self.instance.prompt_yesno(prompt=TELEMETRY_OPT_IN_PROMPT)
-        if choice:
-            cfg_file = os.path.join(state_dir, 'machrc')
-            if update_or_create_build_telemetry_config(cfg_file):
-                print('\nThanks for enabling build telemetry! You can change this setting at ' +
-                      'any time by editing the config file `{}`\n'.format(cfg_file))
-
     def bootstrap(self):
         if self.choice is None:
             # Like ['1. Firefox for Desktop', '2. Firefox for Android Artifact Mode', ...].
             labels = ['%s. %s' % (i + 1, name) for (i, (name, _)) in enumerate(APPLICATIONS_LIST)]
             prompt = APPLICATION_CHOICE % '\n'.join(labels)
             prompt_choice = self.instance.prompt_int(prompt=prompt, low=1, high=len(APPLICATIONS))
             name, application = APPLICATIONS_LIST[prompt_choice-1]
         elif self.choice not in APPLICATIONS.keys():
@@ -410,18 +355,16 @@ class Bootstrapper(object):
             # required to open the repo.
             r = current_firefox_checkout(
                 check_output=self.instance.check_output,
                 env=self.instance._hg_cleanenv(load_hgrc=True),
                 hg=self.instance.which('hg'))
             (checkout_type, checkout_root) = r
             have_clone = bool(checkout_type)
 
-            if state_dir_available:
-                self.check_telemetry_opt_in(state_dir)
             self.maybe_install_private_packages_or_exit(state_dir,
                                                         state_dir_available,
                                                         have_clone,
                                                         checkout_root)
             sys.exit(0)
 
         self.instance.install_system_packages()
 
@@ -487,18 +430,16 @@ class Bootstrapper(object):
                 git = self.instance.which('git')
                 watchman = self.instance.which('watchman')
                 have_clone = git_clone_firefox(git, dest, watchman)
                 checkout_root = dest
 
         if not have_clone:
             print(SOURCE_ADVERTISE)
 
-        if state_dir_available:
-            self.check_telemetry_opt_in(state_dir)
         self.maybe_install_private_packages_or_exit(state_dir,
                                                     state_dir_available,
                                                     have_clone,
                                                     checkout_root)
 
         print(self.finished % name)
         if not (self.instance.which('rustc') and self.instance._parse_version('rustc')
                 >= MODERN_RUST_VERSION):
--- a/python/mozboot/mozboot/mach_commands.py
+++ b/python/mozboot/mozboot/mach_commands.py
@@ -11,40 +11,35 @@ from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 @CommandProvider
 class Bootstrap(object):
     """Bootstrap system and mach for optimal development experience."""
-    def __init__(self, context):
-        self._context = context
 
     @Command('bootstrap', category='devenv',
              description='Install required system packages for building.')
     @CommandArgument('--application-choice',
                      default=None,
                      help='Pass in an application choice (see mozboot.bootstrap.APPLICATIONS) '
                      'instead of using the default interactive prompt.')
     @CommandArgument('--no-interactive', dest='no_interactive', action='store_true',
                      help='Answer yes to any (Y/n) interactive prompts.')
     @CommandArgument('--no-system-changes', dest='no_system_changes',
                      action='store_true',
                      help='Only execute actions that leave the system '
                           'configuration alone.')
     def bootstrap(self, application_choice=None, no_interactive=False, no_system_changes=False):
         from mozboot.bootstrap import Bootstrapper
 
-        bootstrapper = Bootstrapper(
-            choice=application_choice,
-            no_interactive=no_interactive,
-            no_system_changes=no_system_changes,
-            mach_context=self._context,
-        )
+        bootstrapper = Bootstrapper(choice=application_choice,
+                                    no_interactive=no_interactive,
+                                    no_system_changes=no_system_changes)
         bootstrapper.bootstrap()
 
 
 @CommandProvider
 class VersionControlCommands(object):
     def __init__(self, context):
         self._context = context
 
deleted file mode 100644
--- a/python/mozboot/mozboot/test/python.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[DEFAULT]
-skip-if = python == 3
-
-[test_write_config.py]
deleted file mode 100644
--- a/python/mozboot/mozboot/test/test_write_config.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import absolute_import
-
-import mozunit
-import pytest
-
-from mach.config import ConfigSettings
-from mach.decorators import SettingsProvider
-from mozboot.bootstrap import update_or_create_build_telemetry_config
-
-
-# Duplicated from python/mozbuild/mozbuild/mach_commands.py because we can't
-# actually import that module here.
-@SettingsProvider
-class TelemetrySettings():
-    config_settings = [
-        ('build.telemetry', 'boolean', """
-Enable submission of build system telemetry.
-        """.strip(), False),
-    ]
-
-
-@SettingsProvider
-class OtherSettings():
-    config_settings = [
-        ('foo.bar', 'int', '', 1),
-        ('build.abc', 'string', '', ''),
-    ]
-
-
-def read(path):
-    s = ConfigSettings()
-    s.register_provider(TelemetrySettings)
-    s.register_provider(OtherSettings)
-    s.load_file(path)
-    return s
-
-
-@pytest.fixture
-def config_path(tmpdir):
-    return unicode(tmpdir.join('machrc'))
-
-
-@pytest.fixture
-def write_config(config_path):
-    def _config(contents):
-        with open(config_path, 'wb') as f:
-            f.write(contents)
-    return _config
-
-
-def test_nonexistent(config_path):
-    update_or_create_build_telemetry_config(config_path)
-    s = read(config_path)
-    assert(s.build.telemetry)
-
-
-def test_file_exists_no_build_section(config_path, write_config):
-    write_config('''[foo]
-bar = 2
-''')
-    update_or_create_build_telemetry_config(config_path)
-    s = read(config_path)
-    assert(s.build.telemetry)
-    assert(s.foo.bar == 2)
-
-
-def test_existing_build_section(config_path, write_config):
-    write_config('''[foo]
-bar = 2
-
-[build]
-abc = xyz
-''')
-    update_or_create_build_telemetry_config(config_path)
-    s = read(config_path)
-    assert(s.build.telemetry)
-    assert(s.build.abc == 'xyz')
-    assert(s.foo.bar == 2)
-
-
-def test_malformed_file(config_path, write_config):
-    """Ensure that a malformed config file doesn't cause breakage."""
-    write_config('''[foo
-bar = 1
-''')
-    assert(not update_or_create_build_telemetry_config(config_path))
-    # Can't read config, it will not have been written!
-
-
-if __name__ == '__main__':
-    mozunit.main()
--- 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;