Bug 1792387 - part 1: Make `HTMLEditor` join/split node direction switchable by a pref r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 30 Sep 2022 22:20:19 +0000
changeset 636752 fdd1ba8cd07a6c380aa11e57ac985d3b46d795ea
parent 636751 6ecbc2f44299497d2b2b905fe494b047d60fcd47
child 636753 fe6e3d412dd295b0ae8d640ee58b0aae881744de
push id170484
push usermasayuki@d-toybox.com
push dateFri, 30 Sep 2022 22:23:25 +0000
treeherderautoland@f4c48015c119 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1792387
milestone107.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
Bug 1792387 - part 1: Make `HTMLEditor` join/split node direction switchable by a pref r=m_kato Differential Revision: https://phabricator.services.mozilla.com/D158097
editor/composer/nsEditingSession.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
modules/libpref/init/StaticPrefList.yaml
modules/libpref/init/all.js
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -12,17 +12,18 @@
 #include "mozilla/HTMLEditor.h"               // for HTMLEditor
 #include "mozilla/mozalloc.h"                 // for operator new
 #include "mozilla/PresShell.h"                // for PresShell
 #include "nsAString.h"
 #include "nsBaseCommandController.h"  // for nsBaseCommandController
 #include "nsCommandManager.h"         // for nsCommandManager
 #include "nsComponentManagerUtils.h"  // for do_CreateInstance
 #include "nsContentUtils.h"
-#include "nsDebug.h"  // for NS_ENSURE_SUCCESS, etc
+#include "nsDebug.h"     // for NS_ENSURE_SUCCESS, etc
+#include "nsDocShell.h"  // for nsDocShell
 #include "nsEditingSession.h"
 #include "nsError.h"                      // for NS_ERROR_FAILURE, NS_OK, etc
 #include "nsIChannel.h"                   // for nsIChannel
 #include "nsIContentViewer.h"             // for nsIContentViewer
 #include "nsIControllers.h"               // for nsIControllers
 #include "nsID.h"                         // for NS_GET_IID, etc
 #include "nsHTMLDocument.h"               // for nsHTMLDocument
 #include "nsIDocShell.h"                  // for nsIDocShell
@@ -338,19 +339,21 @@ nsresult nsEditingSession::SetupEditorOn
     // setter for web compatibility.  The document editing APIs will tell the
     // developer if editing has been disabled because we're in a document type
     // that doesn't support editing.
     return NS_OK;
   }
 
   // Create editor and do other things
   //  only if we haven't found some error above,
-  nsCOMPtr<nsIDocShell> docShell = aWindow.GetDocShell();
-  NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
-  RefPtr<PresShell> presShell = docShell->GetPresShell();
+  const RefPtr<nsDocShell> docShell = nsDocShell::Cast(aWindow.GetDocShell());
+  if (NS_WARN_IF(!docShell)) {
+    return NS_ERROR_FAILURE;
+  }
+  const RefPtr<PresShell> presShell = docShell->GetPresShell();
   if (NS_WARN_IF(!presShell)) {
     return NS_ERROR_FAILURE;
   }
 
   if (!mInteractive) {
     // Disable animation of images in this document:
     nsPresContext* presContext = presShell->GetPresContext();
     NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
@@ -360,54 +363,59 @@ nsresult nsEditingSession::SetupEditorOn
   }
 
   // Hide selection changes during initialization, in order to hide this
   // from web pages.
   RefPtr<nsFrameSelection> fs = presShell->FrameSelection();
   NS_ENSURE_TRUE(fs, NS_ERROR_FAILURE);
   AutoHideSelectionChanges hideSelectionChanges(fs);
 
+  nsCOMPtr<nsIContentViewer> contentViewer;
+  nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
+  if (NS_FAILED(rv) || NS_WARN_IF(!contentViewer)) {
+    NS_WARNING("nsDocShell::GetContentViewer() failed");
+    return rv;
+  }
+
+  const RefPtr<Document> doc = contentViewer->GetDocument();
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_FAILURE;
+  }
+
   // create and set editor
   // Try to reuse an existing editor
   nsCOMPtr<nsIEditor> editor = do_QueryReferent(mExistingEditor);
   RefPtr<HTMLEditor> htmlEditor = HTMLEditor::GetFrom(editor);
   MOZ_ASSERT_IF(editor, htmlEditor);
   if (htmlEditor) {
     htmlEditor->PreDestroy();
   } else {
-    htmlEditor = new HTMLEditor();
+    htmlEditor = new HTMLEditor(*doc);
     mExistingEditor =
         do_GetWeakReference(static_cast<nsIEditor*>(htmlEditor.get()));
   }
   // set the editor on the docShell. The docShell now owns it.
-  nsresult rv = docShell->SetHTMLEditor(htmlEditor);
+  rv = docShell->SetHTMLEditor(htmlEditor);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // setup the HTML editor command controller
   if (needHTMLController) {
     // The third controller takes an nsIEditor as the context
     rv = SetupEditorCommandController(
         nsBaseCommandController::CreateHTMLEditorController, &aWindow,
         static_cast<nsIEditor*>(htmlEditor), &mHTMLCommandControllerId);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set mimetype on editor
   rv = htmlEditor->SetContentsMIMEType(mimeType);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIContentViewer> contentViewer;
-  rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(contentViewer, NS_ERROR_FAILURE);
-
-  RefPtr<Document> doc = contentViewer->GetDocument();
-  if (NS_WARN_IF(!doc)) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(docShell->HasContentViewer());
+  MOZ_ASSERT(contentViewer->GetDocument());
 
   MOZ_DIAGNOSTIC_ASSERT(commandsUpdater == mComposerCommandsUpdater);
   if (MOZ_UNLIKELY(commandsUpdater != mComposerCommandsUpdater)) {
     commandsUpdater = mComposerCommandsUpdater;
   }
   rv = htmlEditor->Init(*doc, *commandsUpdater, mEditorFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -150,19 +150,36 @@ HTMLEditor::InsertNodeIntoProperAncestor
 template Result<CreateTextResult, nsresult>
 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
     Text& aContentToInsert, const EditorDOMPoint& aPointToInsert,
     SplitAtEdges aSplitAtEdges);
 
 HTMLEditor::InitializeInsertingElement HTMLEditor::DoNothingForNewElement =
     [](HTMLEditor&, Element&, const EditorDOMPoint&) { return NS_OK; };
 
-HTMLEditor::HTMLEditor()
+static bool ShouldUseTraditionalJoinSplitDirection(const Document& aDocument) {
+  if (nsIPrincipal* principal = aDocument.GetPrincipalForPrefBasedHacks()) {
+    if (principal->IsURIInPrefList("editor.join_split_direction."
+                                   "force_use_traditional_direction")) {
+      return true;
+    }
+    if (principal->IsURIInPrefList("editor.join_split_direction."
+                                   "force_use_compatible_direction")) {
+      return false;
+    }
+  }
+  return !StaticPrefs::
+      editor_join_split_direction_compatible_with_the_other_browsers();
+}
+
+HTMLEditor::HTMLEditor(const Document& aDocument)
     : EditorBase(EditorBase::EditorType::HTML),
       mCRInParagraphCreatesParagraph(false),
+      mUseGeckoTraditionalJoinSplitBehavior(
+          ShouldUseTraditionalJoinSplitDirection(aDocument)),
       mIsObjectResizingEnabled(
           StaticPrefs::editor_resizing_enabled_by_default()),
       mIsResizing(false),
       mPreserveRatio(false),
       mResizedObjectIsAnImage(false),
       mIsAbsolutelyPositioningEnabled(
           StaticPrefs::editor_positioning_enabled_by_default()),
       mResizedObjectIsAbsolutelyPositioned(false),
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -119,17 +119,20 @@ class HTMLEditor final : public EditorBa
   NS_DECL_NSIEDITORMAILSUPPORT
 
   // nsITableEditor methods
   NS_DECL_NSITABLEEDITOR
 
   // nsISelectionListener overrides
   NS_DECL_NSISELECTIONLISTENER
 
-  HTMLEditor();
+  /**
+   * @param aDocument   The document whose content will be editable.
+   */
+  explicit HTMLEditor(const Document& aDocument);
 
   /**
    * @param aDocument   The document whose content will be editable.
    * @param aComposerCommandsUpdater     The composer command updater.
    * @param aFlags      Some of nsIEditor::eEditor*Mask flags.
    */
   MOZ_CAN_RUN_SCRIPT nsresult
   Init(Document& aDocument, ComposerCommandsUpdater& aComposerCommandsUpdater,
@@ -4479,17 +4482,17 @@ class HTMLEditor final : public EditorBa
   // mPaddingBRElementForEmptyEditor should be used for placing caret
   // at proper position when editor is empty.
   RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor;
 
   bool mCRInParagraphCreatesParagraph;
 
   // Whether use Blink/WebKit compatible joining nodes and split a node
   // direction or Gecko's traditional direction.
-  bool mUseGeckoTraditionalJoinSplitBehavior = true;
+  bool mUseGeckoTraditionalJoinSplitBehavior;
 
   // resizing
   bool mIsObjectResizingEnabled;
   bool mIsResizing;
   bool mPreserveRatio;
   bool mResizedObjectIsAnImage;
 
   // absolute positioning
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -4676,16 +4676,24 @@
 
 # Whether editor initializes attributes and/or child nodes of newly inserting
 # element before or after connecting to the DOM tree.
 - name: editor.initialize_element_before_connect
   type: bool
   value: true
   mirror: always
 
+# Whether use Blink/WebKit compatbile joining nodes and split a node direction.
+# false: Left node will be created (at splitting) and deleted (at joining)
+# true:  Right node will be created (at splitting) and deleted (at joinining)
+- name: editor.join_split_direction.compatible_with_the_other_browsers
+  type: bool
+  value: false
+  mirror: always
+
 # Delay to mask last input character in password fields.
 # If negative value, to use platform's default behavior.
 # If 0, no delay to mask password.
 # Otherwise, password fields unmask last input character(s) during specified
 # time (in milliseconds).
 - name: editor.password.mask_delay
   type: int32_t
   value: -1
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -872,16 +872,22 @@ pref("print.print_edge_bottom", 0);
 
 // Should this just be checking for MOZ_WIDGET_GTK?
 #if defined(ANDROID) || defined(XP_UNIX) && !defined(XP_MACOSX)
   pref("print.print_reversed", false);
   // This is the default. Probably just remove this.
   pref("print.print_in_color", true);
 #endif
 
+// List of domains of web apps which depend on Gecko's traditional join/split
+// node(s) behavior or Blink/WebKit compatible one in `contenteditable` or
+// `designMode`.
+pref("editor.join_split_direction.force_use_traditional_direction", "");
+pref("editor.join_split_direction.force_use_compatible_direction", "");
+
 // Scripts & Windows prefs
 pref("dom.beforeunload_timeout_ms",         1000);
 pref("dom.disable_window_flip",             false);
 pref("dom.disable_window_move_resize",      false);
 
 pref("dom.allow_scripts_to_close_windows",          false);
 
 pref("dom.popup_allowed_events", "change click dblclick auxclick mousedown mouseup pointerdown pointerup notificationclick reset submit touchend contextmenu");