Bug 1883706: part 3) Implement `createHTML`, `createScript` and `createScriptURL...
[gecko.git] / editor / libeditor / EditorUtils.cpp
blobd9df9338e0755498b8b7c72620f3ebc389621519
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "EditorUtils.h"
8 #include "EditorDOMPoint.h" // for EditorDOMPoint, EditorDOMRange, etc
9 #include "HTMLEditHelpers.h" // for MoveNodeResult
10 #include "HTMLEditUtils.h" // for HTMLEditUtils
11 #include "TextEditor.h" // for TextEditor
13 #include "mozilla/ComputedStyle.h" // for ComputedStyle
14 #include "mozilla/IntegerRange.h" // for IntegerRange
15 #include "mozilla/dom/Document.h" // for dom::Document
16 #include "mozilla/dom/Selection.h" // for dom::Selection
17 #include "mozilla/dom/Text.h" // for dom::Text
19 #include "nsComponentManagerUtils.h" // for do_CreateInstance
20 #include "nsContentUtils.h" // for nsContentUtils
21 #include "nsComputedDOMStyle.h" // for nsComputedDOMStyle
22 #include "nsError.h" // for NS_SUCCESS_* and NS_ERROR_*
23 #include "nsFrameSelection.h" // for nsFrameSelection
24 #include "nsIContent.h" // for nsIContent
25 #include "nsINode.h" // for nsINode
26 #include "nsITransferable.h" // for nsITransferable
27 #include "nsRange.h" // for nsRange
28 #include "nsStyleConsts.h" // for StyleWhiteSpace
29 #include "nsStyleStruct.h" // for nsStyleText, etc
31 namespace mozilla {
33 using namespace dom;
35 /******************************************************************************
36 * mozilla::EditActionResult
37 *****************************************************************************/
39 EditActionResult& EditActionResult::operator|=(
40 const MoveNodeResult& aMoveNodeResult) {
41 mHandled |= aMoveNodeResult.Handled();
42 return *this;
45 /******************************************************************************
46 * some general purpose editor utils
47 *****************************************************************************/
49 bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
50 EditorRawDOMPoint* aOutPoint /* = nullptr */) {
51 if (aOutPoint) {
52 aOutPoint->Clear();
55 if (&aNode == &aParent) {
56 return false;
59 for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
60 if (node->GetParentNode() == &aParent) {
61 if (aOutPoint) {
62 MOZ_ASSERT(node->IsContent());
63 aOutPoint->Set(node->AsContent());
65 return true;
69 return false;
72 bool EditorUtils::IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
73 EditorDOMPoint* aOutPoint) {
74 MOZ_ASSERT(aOutPoint);
75 aOutPoint->Clear();
76 if (&aNode == &aParent) {
77 return false;
80 for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
81 if (node->GetParentNode() == &aParent) {
82 MOZ_ASSERT(node->IsContent());
83 aOutPoint->Set(node->AsContent());
84 return true;
88 return false;
91 // static
92 Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
93 EditorUtils::GetComputedWhiteSpaceStyles(const nsIContent& aContent) {
94 if (MOZ_UNLIKELY(!aContent.IsElement() && !aContent.GetParentElement())) {
95 return Nothing();
97 RefPtr<const ComputedStyle> elementStyle =
98 nsComputedDOMStyle::GetComputedStyleNoFlush(
99 aContent.IsElement() ? aContent.AsElement()
100 : aContent.GetParentElement());
101 if (NS_WARN_IF(!elementStyle)) {
102 return Nothing();
104 const auto* styleText = elementStyle->StyleText();
105 return Some(
106 std::pair(styleText->mWhiteSpaceCollapse, styleText->mTextWrapMode));
109 // static
110 bool EditorUtils::IsWhiteSpacePreformatted(const nsIContent& aContent) {
111 // Look at the node (and its parent if it's not an element), and grab its
112 // ComputedStyle.
113 Element* element = aContent.GetAsElementOrParentElement();
114 if (!element) {
115 return false;
118 RefPtr<const ComputedStyle> elementStyle =
119 nsComputedDOMStyle::GetComputedStyleNoFlush(element);
120 if (!elementStyle) {
121 // Consider nodes without a ComputedStyle to be NOT preformatted:
122 // For instance, this is true of JS tags inside the body (which show
123 // up as #text nodes but have no ComputedStyle).
124 return false;
127 return elementStyle->StyleText()->WhiteSpaceIsSignificant();
130 // static
131 bool EditorUtils::IsNewLinePreformatted(const nsIContent& aContent) {
132 // Look at the node (and its parent if it's not an element), and grab its
133 // ComputedStyle.
134 Element* element = aContent.GetAsElementOrParentElement();
135 if (!element) {
136 return false;
139 RefPtr<const ComputedStyle> elementStyle =
140 nsComputedDOMStyle::GetComputedStyleNoFlush(element);
141 if (!elementStyle) {
142 // Consider nodes without a ComputedStyle to be NOT preformatted:
143 // For instance, this is true of JS tags inside the body (which show
144 // up as #text nodes but have no ComputedStyle).
145 return false;
148 return elementStyle->StyleText()->NewlineIsSignificantStyle();
151 // static
152 bool EditorUtils::IsOnlyNewLinePreformatted(const nsIContent& aContent) {
153 // Look at the node (and its parent if it's not an element), and grab its
154 // ComputedStyle.
155 Element* element = aContent.GetAsElementOrParentElement();
156 if (!element) {
157 return false;
160 RefPtr<const ComputedStyle> elementStyle =
161 nsComputedDOMStyle::GetComputedStyleNoFlush(element);
162 if (!elementStyle) {
163 // Consider nodes without a ComputedStyle to be NOT preformatted:
164 // For instance, this is true of JS tags inside the body (which show
165 // up as #text nodes but have no ComputedStyle).
166 return false;
169 return elementStyle->StyleText()->mWhiteSpaceCollapse ==
170 StyleWhiteSpaceCollapse::PreserveBreaks;
173 // static
174 Result<nsCOMPtr<nsITransferable>, nsresult>
175 EditorUtils::CreateTransferableForPlainText(const Document& aDocument) {
176 // Create generic Transferable for getting the data
177 nsresult rv;
178 nsCOMPtr<nsITransferable> transferable =
179 do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
180 if (NS_FAILED(rv)) {
181 NS_WARNING("do_CreateInstance() failed to create nsITransferable instance");
182 return Err(rv);
185 if (!transferable) {
186 NS_WARNING("do_CreateInstance() returned nullptr, but ignored");
187 return nsCOMPtr<nsITransferable>();
190 DebugOnly<nsresult> rvIgnored =
191 transferable->Init(aDocument.GetLoadContext());
192 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
193 "nsITransferable::Init() failed, but ignored");
195 rvIgnored = transferable->AddDataFlavor(kTextMime);
196 NS_WARNING_ASSERTION(
197 NS_SUCCEEDED(rvIgnored),
198 "nsITransferable::AddDataFlavor(kTextMime) failed, but ignored");
199 rvIgnored = transferable->AddDataFlavor(kMozTextInternal);
200 NS_WARNING_ASSERTION(
201 NS_SUCCEEDED(rvIgnored),
202 "nsITransferable::AddDataFlavor(kMozTextInternal) failed, but ignored");
203 return transferable;
206 /******************************************************************************
207 * mozilla::EditorDOMPointBase
208 *****************************************************************************/
210 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharCollapsibleASCIISpace);
212 template <typename PT, typename CT>
213 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleASCIISpace() const {
214 if (IsCharNewLine()) {
215 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
217 return IsCharASCIISpace() &&
218 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
221 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharCollapsibleNBSP);
223 template <typename PT, typename CT>
224 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleNBSP() const {
225 // TODO: Perhaps, we should return false if neither previous char nor
226 // next char is collapsible white-space or NBSP.
227 return IsCharNBSP() &&
228 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
231 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
232 IsCharCollapsibleASCIISpaceOrNBSP);
234 template <typename PT, typename CT>
235 bool EditorDOMPointBase<PT, CT>::IsCharCollapsibleASCIISpaceOrNBSP() const {
236 if (IsCharNewLine()) {
237 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
239 return IsCharASCIISpaceOrNBSP() &&
240 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
243 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
244 bool, IsPreviousCharCollapsibleASCIISpace);
246 template <typename PT, typename CT>
247 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleASCIISpace() const {
248 if (IsPreviousCharNewLine()) {
249 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
251 return IsPreviousCharASCIISpace() &&
252 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
255 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
256 IsPreviousCharCollapsibleNBSP);
258 template <typename PT, typename CT>
259 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleNBSP() const {
260 return IsPreviousCharNBSP() &&
261 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
264 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
265 bool, IsPreviousCharCollapsibleASCIISpaceOrNBSP);
267 template <typename PT, typename CT>
268 bool EditorDOMPointBase<PT, CT>::IsPreviousCharCollapsibleASCIISpaceOrNBSP()
269 const {
270 if (IsPreviousCharNewLine()) {
271 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
273 return IsPreviousCharASCIISpaceOrNBSP() &&
274 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
277 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
278 IsNextCharCollapsibleASCIISpace);
280 template <typename PT, typename CT>
281 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleASCIISpace() const {
282 if (IsNextCharNewLine()) {
283 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
285 return IsNextCharASCIISpace() &&
286 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
289 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsNextCharCollapsibleNBSP);
291 template <typename PT, typename CT>
292 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleNBSP() const {
293 return IsNextCharNBSP() &&
294 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
297 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
298 bool, IsNextCharCollapsibleASCIISpaceOrNBSP);
300 template <typename PT, typename CT>
301 bool EditorDOMPointBase<PT, CT>::IsNextCharCollapsibleASCIISpaceOrNBSP() const {
302 if (IsNextCharNewLine()) {
303 return !EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
305 return IsNextCharASCIISpaceOrNBSP() &&
306 !EditorUtils::IsWhiteSpacePreformatted(*ContainerAs<Text>());
309 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool, IsCharPreformattedNewLine);
311 template <typename PT, typename CT>
312 bool EditorDOMPointBase<PT, CT>::IsCharPreformattedNewLine() const {
313 return IsCharNewLine() &&
314 EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
317 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
318 bool, IsCharPreformattedNewLineCollapsedWithWhiteSpaces);
320 template <typename PT, typename CT>
321 bool EditorDOMPointBase<
322 PT, CT>::IsCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
323 return IsCharNewLine() &&
324 EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
327 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
328 IsPreviousCharPreformattedNewLine);
330 template <typename PT, typename CT>
331 bool EditorDOMPointBase<PT, CT>::IsPreviousCharPreformattedNewLine() const {
332 return IsPreviousCharNewLine() &&
333 EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
336 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
337 bool, IsPreviousCharPreformattedNewLineCollapsedWithWhiteSpaces);
339 template <typename PT, typename CT>
340 bool EditorDOMPointBase<
341 PT, CT>::IsPreviousCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
342 return IsPreviousCharNewLine() &&
343 EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
346 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(bool,
347 IsNextCharPreformattedNewLine);
349 template <typename PT, typename CT>
350 bool EditorDOMPointBase<PT, CT>::IsNextCharPreformattedNewLine() const {
351 return IsNextCharNewLine() &&
352 EditorUtils::IsNewLinePreformatted(*ContainerAs<Text>());
355 NS_INSTANTIATE_EDITOR_DOM_POINT_CONST_METHOD(
356 bool, IsNextCharPreformattedNewLineCollapsedWithWhiteSpaces);
358 template <typename PT, typename CT>
359 bool EditorDOMPointBase<
360 PT, CT>::IsNextCharPreformattedNewLineCollapsedWithWhiteSpaces() const {
361 return IsNextCharNewLine() &&
362 EditorUtils::IsOnlyNewLinePreformatted(*ContainerAs<Text>());
365 /******************************************************************************
366 * mozilla::EditorDOMRangeBase
367 *****************************************************************************/
369 NS_INSTANTIATE_EDITOR_DOM_RANGE_CONST_METHOD(nsINode*,
370 GetClosestCommonInclusiveAncestor);
372 template <typename EditorDOMPointType>
373 nsINode* EditorDOMRangeBase<
374 EditorDOMPointType>::GetClosestCommonInclusiveAncestor() const {
375 if (NS_WARN_IF(!IsPositioned())) {
376 return nullptr;
378 return nsContentUtils::GetClosestCommonInclusiveAncestor(
379 mStart.GetContainer(), mEnd.GetContainer());
382 /******************************************************************************
383 * mozilla::CaretPoint
384 *****************************************************************************/
386 nsresult CaretPoint::SuggestCaretPointTo(
387 EditorBase& aEditorBase, const SuggestCaretOptions& aOptions) const {
388 mHandledCaretPoint = true;
389 if (!mCaretPoint.IsSet()) {
390 if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion)) {
391 return NS_OK;
393 NS_WARNING("There was no suggestion to put caret");
394 return NS_ERROR_FAILURE;
396 if (aOptions.contains(SuggestCaret::OnlyIfTransactionsAllowedToDoIt) &&
397 !aEditorBase.AllowsTransactionsToChangeSelection()) {
398 return NS_OK;
400 nsresult rv = aEditorBase.CollapseSelectionTo(mCaretPoint);
401 if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
402 NS_WARNING(
403 "EditorBase::CollapseSelectionTo() caused destroying the editor");
404 return NS_ERROR_EDITOR_DESTROYED;
406 return aOptions.contains(SuggestCaret::AndIgnoreTrivialError) && NS_FAILED(rv)
407 ? NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR
408 : rv;
411 bool CaretPoint::CopyCaretPointTo(EditorDOMPoint& aPointToPutCaret,
412 const EditorBase& aEditorBase,
413 const SuggestCaretOptions& aOptions) const {
414 MOZ_ASSERT(!aOptions.contains(SuggestCaret::AndIgnoreTrivialError));
415 mHandledCaretPoint = true;
416 if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion) &&
417 !mCaretPoint.IsSet()) {
418 return false;
420 if (aOptions.contains(SuggestCaret::OnlyIfTransactionsAllowedToDoIt) &&
421 !aEditorBase.AllowsTransactionsToChangeSelection()) {
422 return false;
424 aPointToPutCaret = mCaretPoint;
425 return true;
428 bool CaretPoint::MoveCaretPointTo(EditorDOMPoint& aPointToPutCaret,
429 const EditorBase& aEditorBase,
430 const SuggestCaretOptions& aOptions) {
431 MOZ_ASSERT(!aOptions.contains(SuggestCaret::AndIgnoreTrivialError));
432 mHandledCaretPoint = true;
433 if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion) &&
434 !mCaretPoint.IsSet()) {
435 return false;
437 if (aOptions.contains(SuggestCaret::OnlyIfTransactionsAllowedToDoIt) &&
438 !aEditorBase.AllowsTransactionsToChangeSelection()) {
439 return false;
441 aPointToPutCaret = UnwrapCaretPoint();
442 return true;
445 } // namespace mozilla