Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / editor / libeditor / HTMLAbsPositionEditor.cpp
blobb4be2c69803637c66abfdb81a4fd10cd17a89eae
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "HTMLEditor.h"
7 #include <math.h>
9 #include "CSSEditUtils.h"
10 #include "EditAction.h"
11 #include "EditorLineBreak.h"
12 #include "HTMLEditHelpers.h"
13 #include "HTMLEditorEventListener.h"
14 #include "HTMLEditUtils.h"
16 #include "mozilla/EventListenerManager.h"
17 #include "mozilla/mozalloc.h"
18 #include "mozilla/Preferences.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/StaticPrefs_editor.h"
21 #include "mozilla/dom/AncestorIterator.h"
22 #include "mozilla/dom/Selection.h"
23 #include "mozilla/dom/Element.h"
24 #include "mozilla/dom/EventTarget.h"
25 #include "nsAString.h"
26 #include "nsCOMPtr.h"
27 #include "nsComputedDOMStyle.h"
28 #include "nsDebug.h"
29 #include "nsError.h"
30 #include "nsGkAtoms.h"
31 #include "nsIContent.h"
32 #include "nsROCSSPrimitiveValue.h"
33 #include "nsINode.h"
34 #include "nsIPrincipal.h"
35 #include "nsISupportsImpl.h"
36 #include "nsISupportsUtils.h"
37 #include "nsLiteralString.h"
38 #include "nsReadableUtils.h"
39 #include "nsString.h"
40 #include "nsStringFwd.h"
41 #include "nsStyledElement.h"
42 #include "nscore.h"
43 #include <algorithm>
45 namespace mozilla {
47 using namespace dom;
49 nsresult HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction(
50 bool aEnabled, nsIPrincipal* aPrincipal) {
51 AutoEditActionDataSetter editActionData(
52 *this, EditAction::eSetPositionToAbsoluteOrStatic, aPrincipal);
53 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
54 if (NS_FAILED(rv)) {
55 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
56 "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
57 return rv;
60 const RefPtr<Element> editingHost = ComputeEditingHost();
61 if (!editingHost) {
62 return NS_SUCCESS_DOM_NO_OPERATION;
65 if (aEnabled) {
66 Result<EditActionResult, nsresult> result =
67 SetSelectionToAbsoluteAsSubAction(*editingHost);
68 if (MOZ_UNLIKELY(result.isErr())) {
69 NS_WARNING("HTMLEditor::SetSelectionToAbsoluteAsSubAction() failed");
70 return result.unwrapErr();
72 return NS_OK;
74 Result<EditActionResult, nsresult> result = SetSelectionToStaticAsSubAction();
75 if (MOZ_UNLIKELY(result.isErr())) {
76 NS_WARNING("HTMLEditor::SetSelectionToStaticAsSubAction() failed");
77 return result.unwrapErr();
79 return NS_OK;
82 already_AddRefed<Element>
83 HTMLEditor::GetAbsolutelyPositionedSelectionContainer() const {
84 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
85 if (NS_WARN_IF(!editActionData.CanHandle())) {
86 return nullptr;
89 Element* selectionContainerElement = GetSelectionContainerElement();
90 if (NS_WARN_IF(!selectionContainerElement)) {
91 return nullptr;
94 AutoTArray<RefPtr<Element>, 24> arrayOfParentElements;
95 for (Element* element :
96 selectionContainerElement->InclusiveAncestorsOfType<Element>()) {
97 arrayOfParentElements.AppendElement(element);
100 nsAutoString positionValue;
101 for (RefPtr<Element> element = selectionContainerElement; element;
102 element = element->GetParentElement()) {
103 if (element->IsHTMLElement(nsGkAtoms::html)) {
104 NS_WARNING(
105 "HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached "
106 "<html> element");
107 return nullptr;
109 nsCOMPtr<nsINode> parentNode = element->GetParentNode();
110 nsresult rv = CSSEditUtils::GetComputedProperty(
111 MOZ_KnownLive(*element), *nsGkAtoms::position, positionValue);
112 if (NS_FAILED(rv)) {
113 NS_WARNING(
114 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
115 return nullptr;
117 if (NS_WARN_IF(Destroyed()) ||
118 NS_WARN_IF(parentNode != element->GetParentNode())) {
119 return nullptr;
121 if (positionValue.EqualsLiteral("absolute")) {
122 return element.forget();
125 return nullptr;
128 NS_IMETHODIMP HTMLEditor::GetAbsolutePositioningEnabled(bool* aIsEnabled) {
129 *aIsEnabled = IsAbsolutePositionEditorEnabled();
130 return NS_OK;
133 NS_IMETHODIMP HTMLEditor::SetAbsolutePositioningEnabled(bool aIsEnabled) {
134 EnableAbsolutePositionEditor(aIsEnabled);
135 return NS_OK;
138 NS_IMETHODIMP HTMLEditor::GetIsAbsolutePositioningActive(bool* aIsActive) {
139 MOZ_ASSERT(aIsActive);
140 *aIsActive = !!mAbsolutelyPositionedObject;
141 return NS_OK;
144 Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
145 nsStyledElement& aStyledElement, int32_t aChange) {
146 if (!aChange) {
147 return 0; // XXX Why don't we return current z-index value in this case?
150 int32_t zIndex = GetZIndex(aStyledElement);
151 if (NS_WARN_IF(Destroyed())) {
152 return Err(NS_ERROR_EDITOR_DESTROYED);
154 zIndex = std::max(zIndex + aChange, 0);
155 nsresult rv = SetZIndexWithTransaction(aStyledElement, zIndex);
156 if (rv == NS_ERROR_EDITOR_DESTROYED) {
157 NS_WARNING("HTMLEditor::SetZIndexWithTransaction() destroyed the editor");
158 return Err(NS_ERROR_EDITOR_DESTROYED);
160 NS_WARNING_ASSERTION(
161 NS_SUCCEEDED(rv),
162 "HTMLEditor::SetZIndexWithTransaction() failed, but ignored");
163 return zIndex;
166 nsresult HTMLEditor::SetZIndexWithTransaction(nsStyledElement& aStyledElement,
167 int32_t aZIndex) {
168 nsAutoString zIndexValue;
169 zIndexValue.AppendInt(aZIndex);
171 nsresult rv = CSSEditUtils::SetCSSPropertyWithTransaction(
172 *this, aStyledElement, *nsGkAtoms::z_index, zIndexValue);
173 if (rv == NS_ERROR_EDITOR_DESTROYED) {
174 NS_WARNING(
175 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::z_index) "
176 "destroyed the editor");
177 return NS_ERROR_EDITOR_DESTROYED;
179 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
180 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::"
181 "z_index) failed, but ignored");
182 return NS_OK;
185 nsresult HTMLEditor::AddZIndexAsAction(int32_t aChange,
186 nsIPrincipal* aPrincipal) {
187 MOZ_ASSERT(IsEditActionDataAvailable());
189 AutoEditActionDataSetter editActionData(
190 *this, EditAction::eIncreaseOrDecreaseZIndex, aPrincipal);
191 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
192 if (NS_FAILED(rv)) {
193 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
194 "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
195 return EditorBase::ToGenericNSResult(rv);
198 Result<EditActionResult, nsresult> result = AddZIndexAsSubAction(aChange);
199 if (MOZ_UNLIKELY(result.isErr())) {
200 NS_WARNING("HTMLEditor::AddZIndexAsSubAction() failed");
201 return EditorBase::ToGenericNSResult(result.unwrapErr());
203 return NS_OK;
206 int32_t HTMLEditor::GetZIndex(Element& aElement) {
207 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
208 if (NS_WARN_IF(!editActionData.CanHandle())) {
209 return 0;
212 nsAutoString zIndexValue;
214 nsresult rv = CSSEditUtils::GetSpecifiedProperty(
215 aElement, *nsGkAtoms::z_index, zIndexValue);
216 if (NS_FAILED(rv)) {
217 NS_WARNING("CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::z_index) failed");
218 return 0;
220 if (zIndexValue.EqualsLiteral("auto")) {
221 if (!aElement.GetParentElement()) {
222 NS_WARNING("aElement was an orphan node or the root node");
223 return 0;
225 // we have to look at the positioned ancestors
226 // cf. CSS 2 spec section 9.9.1
227 nsAutoString positionValue;
228 for (RefPtr<Element> element = aElement.GetParentElement(); element;
229 element = element->GetParentElement()) {
230 if (element->IsHTMLElement(nsGkAtoms::body)) {
231 return 0;
233 nsCOMPtr<nsINode> parentNode = element->GetParentElement();
234 nsresult rv = CSSEditUtils::GetComputedProperty(
235 *element, *nsGkAtoms::position, positionValue);
236 if (NS_FAILED(rv)) {
237 NS_WARNING(
238 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
239 return 0;
241 if (NS_WARN_IF(Destroyed()) ||
242 NS_WARN_IF(parentNode != element->GetParentNode())) {
243 return 0;
245 if (!positionValue.EqualsLiteral("absolute")) {
246 continue;
248 // ah, we found one, what's its z-index ? If its z-index is auto,
249 // we have to continue climbing the document's tree
250 rv = CSSEditUtils::GetComputedProperty(*element, *nsGkAtoms::z_index,
251 zIndexValue);
252 if (NS_FAILED(rv)) {
253 NS_WARNING(
254 "CSSEditUtils::GetComputedProperty(nsGkAtoms::z_index) failed");
255 return 0;
257 if (NS_WARN_IF(Destroyed()) ||
258 NS_WARN_IF(parentNode != element->GetParentNode())) {
259 return 0;
261 if (!zIndexValue.EqualsLiteral("auto")) {
262 break;
267 if (zIndexValue.EqualsLiteral("auto")) {
268 return 0;
271 nsresult rvIgnored;
272 int32_t result = zIndexValue.ToInteger(&rvIgnored);
273 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
274 "nsAString::ToInteger() failed, but ignored");
275 return result;
278 bool HTMLEditor::CreateGrabberInternal(nsIContent& aParentContent) {
279 if (NS_WARN_IF(mGrabber)) {
280 return false;
283 mGrabber = CreateAnonymousElement(nsGkAtoms::span, aParentContent,
284 u"mozGrabber"_ns, false);
286 // mGrabber may be destroyed during creation due to there may be
287 // mutation event listener.
288 if (!mGrabber) {
289 NS_WARNING(
290 "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozGrabber) "
291 "failed");
292 return false;
295 EventListenerManager* eventListenerManager =
296 mGrabber->GetOrCreateListenerManager();
297 eventListenerManager->AddEventListenerByType(
298 mEventListener, u"mousedown"_ns, TrustedEventsAtSystemGroupBubble());
299 MOZ_ASSERT(mGrabber);
300 return true;
303 nsresult HTMLEditor::RefreshGrabberInternal() {
304 MOZ_ASSERT(IsEditActionDataAvailable());
306 if (!mAbsolutelyPositionedObject) {
307 return NS_OK;
310 OwningNonNull<Element> absolutelyPositionedObject =
311 *mAbsolutelyPositionedObject;
312 nsresult rv = GetPositionAndDimensions(
313 absolutelyPositionedObject, mPositionedObjectX, mPositionedObjectY,
314 mPositionedObjectWidth, mPositionedObjectHeight,
315 mPositionedObjectBorderLeft, mPositionedObjectBorderTop,
316 mPositionedObjectMarginLeft, mPositionedObjectMarginTop);
317 if (NS_FAILED(rv)) {
318 NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed");
319 return rv;
321 if (NS_WARN_IF(absolutelyPositionedObject != mAbsolutelyPositionedObject)) {
322 return NS_ERROR_FAILURE;
325 RefPtr<nsStyledElement> grabberStyledElement =
326 nsStyledElement::FromNodeOrNull(mGrabber.get());
327 if (!grabberStyledElement) {
328 return NS_OK;
330 rv = SetAnonymousElementPositionWithoutTransaction(
331 *grabberStyledElement, mPositionedObjectX + 12, mPositionedObjectY - 14);
332 if (NS_WARN_IF(Destroyed())) {
333 return NS_ERROR_EDITOR_DESTROYED;
335 if (NS_FAILED(rv)) {
336 NS_WARNING(
337 "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() failed");
338 return rv;
340 if (NS_WARN_IF(grabberStyledElement != mGrabber.get())) {
341 return NS_ERROR_FAILURE;
343 return NS_OK;
346 void HTMLEditor::HideGrabberInternal() {
347 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
348 return;
351 // Move all members to the local variables first since mutation event
352 // listener may try to show grabber while we're hiding them.
353 RefPtr<Element> absolutePositioningObject =
354 std::move(mAbsolutelyPositionedObject);
355 ManualNACPtr grabber = std::move(mGrabber);
356 ManualNACPtr positioningShadow = std::move(mPositioningShadow);
358 // If we're still in dragging mode, it means that the dragging is canceled
359 // by the web app.
360 if (mGrabberClicked || mIsMoving) {
361 mGrabberClicked = false;
362 mIsMoving = false;
363 if (mEventListener) {
364 DebugOnly<nsresult> rvIgnored =
365 static_cast<HTMLEditorEventListener*>(mEventListener.get())
366 ->ListenToMouseMoveEventForGrabber(false);
367 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
368 "HTMLEditorEventListener::"
369 "ListenToMouseMoveEventForGrabber(false) failed");
373 DebugOnly<nsresult> rv = absolutePositioningObject->UnsetAttr(
374 kNameSpaceID_None, nsGkAtoms::_moz_abspos, true);
375 NS_WARNING_ASSERTION(
376 NS_SUCCEEDED(rv),
377 "Element::UnsetAttr(nsGkAtoms::_moz_abspos) failed, but ignored");
379 // We allow the pres shell to be null; when it is, we presume there
380 // are no document observers to notify, but we still want to
381 // UnbindFromTree.
382 RefPtr<PresShell> presShell = GetPresShell();
383 if (grabber) {
384 DeleteRefToAnonymousNode(std::move(grabber), presShell);
386 if (positioningShadow) {
387 DeleteRefToAnonymousNode(std::move(positioningShadow), presShell);
391 nsresult HTMLEditor::ShowGrabberInternal(Element& aElement) {
392 MOZ_ASSERT(IsEditActionDataAvailable());
394 const RefPtr<Element> editingHost = ComputeEditingHost();
395 if (NS_WARN_IF(!editingHost) ||
396 NS_WARN_IF(!aElement.IsInclusiveDescendantOf(editingHost))) {
397 return NS_ERROR_UNEXPECTED;
400 if (NS_WARN_IF(mGrabber)) {
401 return NS_ERROR_UNEXPECTED;
404 nsAutoString classValue;
405 nsresult rv =
406 GetTemporaryStyleForFocusedPositionedElement(aElement, classValue);
407 if (NS_FAILED(rv)) {
408 NS_WARNING(
409 "HTMLEditor::GetTemporaryStyleForFocusedPositionedElement() failed");
410 return rv;
413 rv = aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos, classValue,
414 true);
415 if (NS_FAILED(rv)) {
416 NS_WARNING("Element::SetAttr(nsGkAtoms::_moz_abspos) failed");
417 return rv;
420 mAbsolutelyPositionedObject = &aElement;
422 Element* parentElement = aElement.GetParentElement();
423 if (NS_WARN_IF(!parentElement)) {
424 return NS_ERROR_FAILURE;
427 if (!CreateGrabberInternal(*parentElement)) {
428 NS_WARNING("HTMLEditor::CreateGrabberInternal() failed");
429 return NS_ERROR_FAILURE;
432 // If we succeeded to create the grabber, HideGrabberInternal() hasn't been
433 // called yet. So, mAbsolutelyPositionedObject should be non-nullptr.
434 MOZ_ASSERT(mAbsolutelyPositionedObject);
436 // Finally, move the grabber to proper position.
437 rv = RefreshGrabberInternal();
438 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
439 "HTMLEditor::RefereshGrabberInternal() failed");
440 return rv;
443 nsresult HTMLEditor::StartMoving() {
444 MOZ_ASSERT(mGrabber);
446 RefPtr<Element> parentElement = mGrabber->GetParentElement();
447 if (NS_WARN_IF(!parentElement) || NS_WARN_IF(!mAbsolutelyPositionedObject)) {
448 return NS_ERROR_FAILURE;
451 // now, let's create the resizing shadow
452 mPositioningShadow =
453 CreateShadow(*parentElement, *mAbsolutelyPositionedObject);
454 if (!mPositioningShadow) {
455 NS_WARNING("HTMLEditor::CreateShadow() failed");
456 return NS_ERROR_FAILURE;
458 if (!mAbsolutelyPositionedObject) {
459 NS_WARNING("The target has gone during HTMLEditor::CreateShadow()");
460 return NS_ERROR_FAILURE;
462 RefPtr<Element> positioningShadow = mPositioningShadow.get();
463 RefPtr<Element> absolutelyPositionedObject = mAbsolutelyPositionedObject;
464 nsresult rv =
465 SetShadowPosition(*positioningShadow, *absolutelyPositionedObject,
466 mPositionedObjectX, mPositionedObjectY);
467 if (NS_FAILED(rv)) {
468 NS_WARNING("HTMLEditor::SetShadowPosition() failed");
469 return rv;
472 // make the shadow appear
473 DebugOnly<nsresult> rvIgnored =
474 mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
475 NS_WARNING_ASSERTION(
476 NS_SUCCEEDED(rvIgnored),
477 "Element::UnsetAttr(nsGkAtoms::_class) failed, but ignored");
479 // position it
480 if (RefPtr<nsStyledElement> positioningShadowStyledElement =
481 nsStyledElement::FromNode(mPositioningShadow.get())) {
482 nsresult rv;
483 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
484 *positioningShadowStyledElement, *nsGkAtoms::width,
485 mPositionedObjectWidth);
486 if (rv == NS_ERROR_EDITOR_DESTROYED) {
487 NS_WARNING(
488 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
489 "nsGkAtoms::width) destroyed the editor");
490 return NS_ERROR_EDITOR_DESTROYED;
492 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
493 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
494 "nsGkAtoms::width) failed, but ignored");
495 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
496 *positioningShadowStyledElement, *nsGkAtoms::height,
497 mPositionedObjectHeight);
498 if (rv == NS_ERROR_EDITOR_DESTROYED) {
499 NS_WARNING(
500 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
501 "nsGkAtoms::height) destroyed the editor");
502 return NS_ERROR_EDITOR_DESTROYED;
504 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
505 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
506 "nsGkAtoms::height) failed, but ignored");
509 mIsMoving = true;
510 return NS_OK; // XXX Looks like nobody refers this result
513 void HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) const {
514 if (mSnapToGridEnabled && mGridSize) {
515 newX = (int32_t)floor(((float)newX / (float)mGridSize) + 0.5f) * mGridSize;
516 newY = (int32_t)floor(((float)newY / (float)mGridSize) + 0.5f) * mGridSize;
520 nsresult HTMLEditor::GrabberClicked() {
521 if (NS_WARN_IF(!mEventListener)) {
522 return NS_ERROR_NOT_INITIALIZED;
524 nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
525 ->ListenToMouseMoveEventForGrabber(true);
526 if (NS_FAILED(rv)) {
527 NS_WARNING(
528 "HTMLEditorEventListener::ListenToMouseMoveEventForGrabber(true) "
529 "failed, but ignored");
530 return NS_OK;
532 mGrabberClicked = true;
533 return NS_OK;
536 nsresult HTMLEditor::EndMoving() {
537 if (mPositioningShadow) {
538 RefPtr<PresShell> presShell = GetPresShell();
539 if (NS_WARN_IF(!presShell)) {
540 return NS_ERROR_NOT_INITIALIZED;
543 DeleteRefToAnonymousNode(std::move(mPositioningShadow), presShell);
545 mPositioningShadow = nullptr;
548 if (mEventListener) {
549 DebugOnly<nsresult> rvIgnored =
550 static_cast<HTMLEditorEventListener*>(mEventListener.get())
551 ->ListenToMouseMoveEventForGrabber(false);
552 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
553 "HTMLEditorEventListener::"
554 "ListenToMouseMoveEventForGrabber(false) failed");
557 mGrabberClicked = false;
558 mIsMoving = false;
559 nsresult rv = RefreshEditingUI();
560 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
561 "HTMLEditor::RefreshEditingUI() failed");
562 return rv;
565 nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) {
566 MOZ_ASSERT(IsEditActionDataAvailable());
568 nsresult rv = EndMoving();
569 if (NS_FAILED(rv)) {
570 NS_WARNING("HTMLEditor::EndMoving() failed");
571 return rv;
574 // we have now to set the new width and height of the resized object
575 // we don't set the x and y position because we don't control that in
576 // a normal HTML layout
577 int32_t newX = mPositionedObjectX + aX - mOriginalX -
578 (mPositionedObjectBorderLeft + mPositionedObjectMarginLeft);
579 int32_t newY = mPositionedObjectY + aY - mOriginalY -
580 (mPositionedObjectBorderTop + mPositionedObjectMarginTop);
582 SnapToGrid(newX, newY);
584 nsAutoString x, y;
585 x.AppendInt(newX);
586 y.AppendInt(newY);
588 // we want one transaction only from a user's point of view
589 AutoPlaceholderBatch treatAsOneTransaction(
590 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
592 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
593 return NS_ERROR_FAILURE;
595 if (RefPtr<nsStyledElement> styledAbsolutelyPositionedElement =
596 nsStyledElement::FromNode(mAbsolutelyPositionedObject)) {
597 nsresult rv;
598 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
599 *this, *styledAbsolutelyPositionedElement, *nsGkAtoms::top, newY);
600 if (rv == NS_ERROR_EDITOR_DESTROYED) {
601 NS_WARNING(
602 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
603 "destroyed the editor");
604 return NS_ERROR_EDITOR_DESTROYED;
606 NS_WARNING_ASSERTION(
607 NS_SUCCEEDED(rv),
608 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
609 "failed, but ignored");
610 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
611 *this, *styledAbsolutelyPositionedElement, *nsGkAtoms::left, newX);
612 if (rv == NS_ERROR_EDITOR_DESTROYED) {
613 NS_WARNING(
614 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
615 "destroyed the editor");
616 return NS_ERROR_EDITOR_DESTROYED;
618 NS_WARNING_ASSERTION(
619 NS_SUCCEEDED(rv),
620 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
621 "failed, but ignored");
623 // keep track of that size
624 mPositionedObjectX = newX;
625 mPositionedObjectY = newY;
627 rv = RefreshResizersInternal();
628 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
629 "HTMLEditor::RefreshResizersInternal() failed");
630 return rv;
633 nsresult HTMLEditor::SetPositionToAbsoluteOrStatic(Element& aElement,
634 bool aEnabled) {
635 nsAutoString positionValue;
636 DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetComputedProperty(
637 aElement, *nsGkAtoms::position, positionValue);
638 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
639 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) "
640 "failed, but ignored");
641 // nothing to do if the element is already in the state we want
642 if (positionValue.EqualsLiteral("absolute") == aEnabled) {
643 return NS_OK;
646 if (aEnabled) {
647 nsresult rv = SetPositionToAbsolute(aElement);
648 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
649 "HTMLEditor::SetPositionToAbsolute() failed");
650 return rv;
653 nsresult rv = SetPositionToStatic(aElement);
654 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
655 "HTMLEditor::SetPositionToStatic() failed");
656 return rv;
659 nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
660 MOZ_ASSERT(IsEditActionDataAvailable());
662 AutoPlaceholderBatch treatAsOneTransaction(
663 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
665 int32_t x, y;
666 DebugOnly<nsresult> rvIgnored = GetElementOrigin(aElement, x, y);
667 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
668 "HTMLEditor::GetElementOrigin() failed, but ignored");
670 nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
671 if (styledElement) {
672 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
673 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
674 nsresult rv = CSSEditUtils::SetCSSPropertyWithTransaction(
675 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position,
676 u"absolute"_ns);
677 if (rv == NS_ERROR_EDITOR_DESTROYED) {
678 NS_WARNING(
679 "CSSEditUtils::SetCSSProperyWithTransaction(nsGkAtoms::Position) "
680 "destroyed the editor");
681 return NS_ERROR_EDITOR_DESTROYED;
683 NS_WARNING_ASSERTION(
684 NS_SUCCEEDED(rvIgnored),
685 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::position, "
686 "absolute) failed, but ignored");
689 SnapToGrid(x, y);
690 if (styledElement) {
691 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
692 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
693 nsresult rv =
694 SetTopAndLeftWithTransaction(MOZ_KnownLive(*styledElement), x, y);
695 if (NS_FAILED(rv)) {
696 NS_WARNING("HTMLEditor::SetTopAndLeftWithTransaction() failed");
697 return rv;
701 // we may need to create a br if the positioned element is alone in its
702 // container
703 nsINode* parentNode = aElement.GetParentNode();
704 if (parentNode->GetChildCount() != 1) {
705 return NS_OK;
707 Result<CreateLineBreakResult, nsresult> insertBRElementResultOrError =
708 InsertLineBreak(WithTransaction::Yes, LineBreakType::BRElement,
709 EditorDOMPoint(parentNode, 0u));
710 if (MOZ_UNLIKELY(insertBRElementResultOrError.isErr())) {
711 NS_WARNING(
712 "HTMLEditor::InsertLineBreak(WithTransaction::Yes, "
713 "LineBreakType::BRElement) failed");
714 return insertBRElementResultOrError.unwrapErr();
716 CreateLineBreakResult insertBRElementResult =
717 insertBRElementResultOrError.unwrap();
718 MOZ_ASSERT(insertBRElementResult.Handled());
719 // XXX Is this intentional selection change?
720 nsresult rv = insertBRElementResult.SuggestCaretPointTo(
721 *this, {SuggestCaret::OnlyIfHasSuggestion,
722 SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
723 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
724 "CreateElementResult::SuggestCaretPointTo() failed");
725 return rv;
728 nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
729 nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
730 if (NS_WARN_IF(!styledElement)) {
731 return NS_ERROR_INVALID_ARG;
734 AutoPlaceholderBatch treatAsOneTransaction(
735 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
737 nsresult rv;
738 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
739 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
740 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
741 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position, u""_ns);
742 if (rv == NS_ERROR_EDITOR_DESTROYED) {
743 NS_WARNING(
744 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
745 "destroyed the editor");
746 return NS_ERROR_EDITOR_DESTROYED;
748 NS_WARNING_ASSERTION(
749 NS_SUCCEEDED(rv),
750 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
751 "failed, but ignored");
752 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
753 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
754 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
755 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::top, u""_ns);
756 if (rv == NS_ERROR_EDITOR_DESTROYED) {
757 NS_WARNING(
758 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
759 "destroyed the editor");
760 return NS_ERROR_EDITOR_DESTROYED;
762 NS_WARNING_ASSERTION(
763 NS_SUCCEEDED(rv),
764 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
765 "failed, but ignored");
766 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
767 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
768 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
769 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::left, u""_ns);
770 if (rv == NS_ERROR_EDITOR_DESTROYED) {
771 NS_WARNING(
772 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
773 "destroyed the editor");
774 return NS_ERROR_EDITOR_DESTROYED;
776 NS_WARNING_ASSERTION(
777 NS_SUCCEEDED(rv),
778 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
779 "failed, but ignored");
780 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
781 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
782 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
783 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::z_index, u""_ns);
784 if (rv == NS_ERROR_EDITOR_DESTROYED) {
785 NS_WARNING(
786 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
787 "destroyed the editor");
788 return NS_ERROR_EDITOR_DESTROYED;
790 NS_WARNING_ASSERTION(
791 NS_SUCCEEDED(rv),
792 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
793 "failed, but ignored");
795 if (!HTMLEditUtils::IsImage(styledElement)) {
796 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
797 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
798 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
799 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::width, u""_ns);
800 if (rv == NS_ERROR_EDITOR_DESTROYED) {
801 NS_WARNING(
802 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
803 "destroyed the editor");
804 return NS_ERROR_EDITOR_DESTROYED;
806 NS_WARNING_ASSERTION(
807 NS_SUCCEEDED(rv),
808 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
809 "failed, but ignored");
810 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
811 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
812 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
813 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::height, u""_ns);
814 if (rv == NS_ERROR_EDITOR_DESTROYED) {
815 NS_WARNING(
816 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
817 "destroyed the editor");
818 return NS_ERROR_EDITOR_DESTROYED;
820 NS_WARNING_ASSERTION(
821 NS_SUCCEEDED(rv),
822 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
823 "failed, but ignored");
826 if (!styledElement->IsHTMLElement(nsGkAtoms::div) ||
827 HTMLEditor::HasStyleOrIdOrClassAttribute(*styledElement)) {
828 return NS_OK;
831 EditorDOMPoint pointToPutCaret;
832 // Make sure the first fild and last child of aElement starts/ends hard
833 // line(s) even after removing `aElement`.
835 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
836 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
837 Result<CreateElementResult, nsresult>
838 maybeInsertBRElementBeforeFirstChildResult =
839 EnsureHardLineBeginsWithFirstChildOf(MOZ_KnownLive(*styledElement));
840 if (MOZ_UNLIKELY(maybeInsertBRElementBeforeFirstChildResult.isErr())) {
841 NS_WARNING("HTMLEditor::EnsureHardLineBeginsWithFirstChildOf() failed");
842 return maybeInsertBRElementBeforeFirstChildResult.unwrapErr();
844 CreateElementResult unwrappedResult =
845 maybeInsertBRElementBeforeFirstChildResult.unwrap();
846 if (unwrappedResult.HasCaretPointSuggestion()) {
847 pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
851 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
852 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
853 Result<CreateElementResult, nsresult>
854 maybeInsertBRElementAfterLastChildResult =
855 EnsureHardLineEndsWithLastChildOf(MOZ_KnownLive(*styledElement));
856 if (MOZ_UNLIKELY(maybeInsertBRElementAfterLastChildResult.isErr())) {
857 NS_WARNING("HTMLEditor::EnsureHardLineEndsWithLastChildOf() failed");
858 return maybeInsertBRElementAfterLastChildResult.unwrapErr();
860 CreateElementResult unwrappedResult =
861 maybeInsertBRElementAfterLastChildResult.unwrap();
862 if (unwrappedResult.HasCaretPointSuggestion()) {
863 pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
867 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
868 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
869 Result<EditorDOMPoint, nsresult> unwrapStyledElementResult =
870 RemoveContainerWithTransaction(MOZ_KnownLive(*styledElement));
871 if (MOZ_UNLIKELY(unwrapStyledElementResult.isErr())) {
872 NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
873 return unwrapStyledElementResult.unwrapErr();
875 if (unwrapStyledElementResult.inspect().IsSet()) {
876 pointToPutCaret = unwrapStyledElementResult.unwrap();
879 if (!AllowsTransactionsToChangeSelection() || !pointToPutCaret.IsSet()) {
880 return NS_OK;
882 rv = CollapseSelectionTo(pointToPutCaret);
883 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
884 "EditorBase::CollapseSelectionTo() failed");
885 return rv;
888 NS_IMETHODIMP HTMLEditor::SetSnapToGridEnabled(bool aEnabled) {
889 mSnapToGridEnabled = aEnabled;
890 return NS_OK;
893 NS_IMETHODIMP HTMLEditor::GetSnapToGridEnabled(bool* aIsEnabled) {
894 *aIsEnabled = mSnapToGridEnabled;
895 return NS_OK;
898 NS_IMETHODIMP HTMLEditor::SetGridSize(uint32_t aSize) {
899 mGridSize = aSize;
900 return NS_OK;
903 NS_IMETHODIMP HTMLEditor::GetGridSize(uint32_t* aSize) {
904 *aSize = mGridSize;
905 return NS_OK;
908 nsresult HTMLEditor::SetTopAndLeftWithTransaction(
909 nsStyledElement& aStyledElement, int32_t aX, int32_t aY) {
910 AutoPlaceholderBatch treatAsOneTransaction(
911 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
912 nsresult rv;
913 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
914 *nsGkAtoms::left, aX);
915 if (rv == NS_ERROR_EDITOR_DESTROYED) {
916 NS_WARNING(
917 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
918 "destroyed the editor");
919 return NS_ERROR_EDITOR_DESTROYED;
921 NS_WARNING_ASSERTION(
922 NS_SUCCEEDED(rv),
923 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
924 "failed, but ignored");
925 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
926 *nsGkAtoms::top, aY);
927 if (rv == NS_ERROR_EDITOR_DESTROYED) {
928 NS_WARNING(
929 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
930 "destroyed the editor");
931 return NS_ERROR_EDITOR_DESTROYED;
933 NS_WARNING_ASSERTION(
934 NS_SUCCEEDED(rv),
935 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
936 "failed, but ignored");
937 return NS_OK;
940 nsresult HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(
941 Element& aElement, nsAString& aReturn) {
942 // we are going to outline the positioned element and bring it to the
943 // front to overlap any other element intersecting with it. But
944 // first, let's see what's the background and foreground colors of the
945 // positioned element.
946 // if background-image computed value is 'none,
947 // If the background color is 'auto' and R G B values of the foreground are
948 // each above #d0, use a black background
949 // If the background color is 'auto' and at least one of R G B values of
950 // the foreground is below #d0, use a white background
951 // Otherwise don't change background/foreground
952 aReturn.Truncate();
954 nsAutoString backgroundImageValue;
955 nsresult rv = CSSEditUtils::GetComputedProperty(
956 aElement, *nsGkAtoms::background_image, backgroundImageValue);
957 if (NS_FAILED(rv)) {
958 NS_WARNING(
959 "CSSEditUtils::GetComputedProperty(nsGkAtoms::background_image) "
960 "failed");
961 return rv;
963 if (!backgroundImageValue.EqualsLiteral("none")) {
964 return NS_OK;
967 nsAutoString backgroundColorValue;
968 rv = CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::backgroundColor,
969 backgroundColorValue);
970 if (NS_FAILED(rv)) {
971 NS_WARNING(
972 "CSSEditUtils::GetComputedProperty(nsGkAtoms::backgroundColor) "
973 "failed");
974 return rv;
976 if (!backgroundColorValue.EqualsLiteral("rgba(0, 0, 0, 0)")) {
977 return NS_OK;
980 RefPtr<const ComputedStyle> style =
981 nsComputedDOMStyle::GetComputedStyle(&aElement);
982 if (NS_WARN_IF(Destroyed())) {
983 return NS_ERROR_EDITOR_DESTROYED;
985 if (!style) {
986 NS_WARNING("nsComputedDOMStyle::GetComputedStyle() failed");
987 return NS_ERROR_FAILURE;
990 static const uint8_t kBlackBgTrigger = 0xd0;
992 auto color = style->StyleText()->mColor.ToColor();
993 if (NS_GET_R(color) >= kBlackBgTrigger &&
994 NS_GET_G(color) >= kBlackBgTrigger &&
995 NS_GET_B(color) >= kBlackBgTrigger) {
996 aReturn.AssignLiteral("black");
997 } else {
998 aReturn.AssignLiteral("white");
1001 return NS_OK;
1004 } // namespace mozilla