Bug 1712849 [wpt PR 29110] - Keep 3D points in a quad coplanar when clamping them...
[gecko.git] / editor / libeditor / HTMLAbsPositionEditor.cpp
blobcabf126faa40e25547b469186578a2a2e0154a18
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 "HTMLEditorEventListener.h"
10 #include "HTMLEditUtils.h"
11 #include "mozilla/EditAction.h"
12 #include "mozilla/EventListenerManager.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/PresShell.h"
15 #include "mozilla/dom/AncestorIterator.h"
16 #include "mozilla/dom/Selection.h"
17 #include "mozilla/dom/Element.h"
18 #include "mozilla/dom/EventTarget.h"
19 #include "mozilla/mozalloc.h"
20 #include "nsAString.h"
21 #include "nsAlgorithm.h"
22 #include "nsCOMPtr.h"
23 #include "nsComputedDOMStyle.h"
24 #include "nsDebug.h"
25 #include "nsError.h"
26 #include "nsGkAtoms.h"
27 #include "nsIContent.h"
28 #include "nsROCSSPrimitiveValue.h"
29 #include "nsINode.h"
30 #include "nsIPrincipal.h"
31 #include "nsISupportsImpl.h"
32 #include "nsISupportsUtils.h"
33 #include "nsLiteralString.h"
34 #include "nsReadableUtils.h"
35 #include "nsString.h"
36 #include "nsStringFwd.h"
37 #include "nsStyledElement.h"
38 #include "nscore.h"
39 #include <algorithm>
41 namespace mozilla {
43 using namespace dom;
45 nsresult HTMLEditor::SetSelectionToAbsoluteOrStaticAsAction(
46 bool aEnabled, nsIPrincipal* aPrincipal) {
47 MOZ_ASSERT(IsEditActionDataAvailable());
49 AutoEditActionDataSetter editActionData(
50 *this, EditAction::eSetPositionToAbsoluteOrStatic, aPrincipal);
51 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
52 if (NS_FAILED(rv)) {
53 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
54 "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
55 return rv;
58 if (aEnabled) {
59 EditActionResult result = SetSelectionToAbsoluteAsSubAction();
60 NS_WARNING_ASSERTION(
61 result.Succeeded(),
62 "HTMLEditor::SetSelectionToAbsoluteAsSubAction() failed");
63 return result.Rv();
65 EditActionResult result = SetSelectionToStaticAsSubAction();
66 NS_WARNING_ASSERTION(result.Succeeded(),
67 "HTMLEditor::SetSelectionToStaticAsSubAction() failed");
68 return result.Rv();
71 already_AddRefed<Element>
72 HTMLEditor::GetAbsolutelyPositionedSelectionContainer() const {
73 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
74 if (NS_WARN_IF(!editActionData.CanHandle())) {
75 return nullptr;
78 Element* selectionContainerElement = GetSelectionContainerElement();
79 if (NS_WARN_IF(!selectionContainerElement)) {
80 return nullptr;
83 AutoTArray<RefPtr<Element>, 24> arrayOfParentElements;
84 for (Element* element :
85 selectionContainerElement->InclusiveAncestorsOfType<Element>()) {
86 arrayOfParentElements.AppendElement(element);
89 nsAutoString positionValue;
90 for (RefPtr<Element> element = selectionContainerElement; element;
91 element = element->GetParentElement()) {
92 if (element->IsHTMLElement(nsGkAtoms::html)) {
93 NS_WARNING(
94 "HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached "
95 "<html> element");
96 return nullptr;
98 nsCOMPtr<nsINode> parentNode = element->GetParentNode();
99 nsresult rv = CSSEditUtils::GetComputedProperty(
100 MOZ_KnownLive(*element), *nsGkAtoms::position, positionValue);
101 if (NS_FAILED(rv)) {
102 NS_WARNING(
103 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
104 return nullptr;
106 if (NS_WARN_IF(Destroyed()) ||
107 NS_WARN_IF(parentNode != element->GetParentNode())) {
108 return nullptr;
110 if (positionValue.EqualsLiteral("absolute")) {
111 return element.forget();
114 return nullptr;
117 NS_IMETHODIMP HTMLEditor::GetAbsolutePositioningEnabled(bool* aIsEnabled) {
118 *aIsEnabled = IsAbsolutePositionEditorEnabled();
119 return NS_OK;
122 NS_IMETHODIMP HTMLEditor::SetAbsolutePositioningEnabled(bool aIsEnabled) {
123 EnableAbsolutePositionEditor(aIsEnabled);
124 return NS_OK;
127 Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
128 nsStyledElement& aStyledElement, int32_t aChange) {
129 if (!aChange) {
130 return 0; // XXX Why don't we return current z-index value in this case?
133 int32_t zIndex = GetZIndex(aStyledElement);
134 if (NS_WARN_IF(Destroyed())) {
135 return Err(NS_ERROR_EDITOR_DESTROYED);
137 zIndex = std::max(zIndex + aChange, 0);
138 nsresult rv = SetZIndexWithTransaction(aStyledElement, zIndex);
139 if (rv == NS_ERROR_EDITOR_DESTROYED) {
140 NS_WARNING("HTMLEditor::SetZIndexWithTransaction() destroyed the editor");
141 return Err(NS_ERROR_EDITOR_DESTROYED);
143 NS_WARNING_ASSERTION(
144 NS_SUCCEEDED(rv),
145 "HTMLEditor::SetZIndexWithTransaction() failed, but ignored");
146 return zIndex;
149 nsresult HTMLEditor::SetZIndexWithTransaction(nsStyledElement& aStyledElement,
150 int32_t aZIndex) {
151 nsAutoString zIndexValue;
152 zIndexValue.AppendInt(aZIndex);
154 nsresult rv = mCSSEditUtils->SetCSSPropertyWithTransaction(
155 aStyledElement, *nsGkAtoms::z_index, zIndexValue);
156 if (rv == NS_ERROR_EDITOR_DESTROYED) {
157 NS_WARNING(
158 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::z_index) "
159 "destroyed the editor");
160 return NS_ERROR_EDITOR_DESTROYED;
162 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
163 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::"
164 "z_index) failed, but ignored");
165 return NS_OK;
168 nsresult HTMLEditor::AddZIndexAsAction(int32_t aChange,
169 nsIPrincipal* aPrincipal) {
170 MOZ_ASSERT(IsEditActionDataAvailable());
172 AutoEditActionDataSetter editActionData(
173 *this, EditAction::eIncreaseOrDecreaseZIndex, aPrincipal);
174 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
175 if (NS_FAILED(rv)) {
176 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
177 "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
178 return EditorBase::ToGenericNSResult(rv);
181 EditActionResult result = AddZIndexAsSubAction(aChange);
182 NS_WARNING_ASSERTION(result.Succeeded(),
183 "HTMLEditor::AddZIndexAsSubAction() failed");
184 return EditorBase::ToGenericNSResult(result.Rv());
187 int32_t HTMLEditor::GetZIndex(Element& aElement) {
188 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
189 if (NS_WARN_IF(!editActionData.CanHandle())) {
190 return 0;
193 nsAutoString zIndexValue;
195 nsresult rv = CSSEditUtils::GetSpecifiedProperty(
196 aElement, *nsGkAtoms::z_index, zIndexValue);
197 if (NS_FAILED(rv)) {
198 NS_WARNING("CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::z_index) failed");
199 return 0;
201 if (zIndexValue.EqualsLiteral("auto")) {
202 if (!aElement.GetParentElement()) {
203 NS_WARNING("aElement was an orphan node or the root node");
204 return 0;
206 // we have to look at the positioned ancestors
207 // cf. CSS 2 spec section 9.9.1
208 nsAutoString positionValue;
209 for (RefPtr<Element> element = aElement.GetParentElement(); element;
210 element = element->GetParentElement()) {
211 if (element->IsHTMLElement(nsGkAtoms::body)) {
212 return 0;
214 nsCOMPtr<nsINode> parentNode = element->GetParentElement();
215 nsresult rv = CSSEditUtils::GetComputedProperty(
216 *element, *nsGkAtoms::position, positionValue);
217 if (NS_FAILED(rv)) {
218 NS_WARNING(
219 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
220 return 0;
222 if (NS_WARN_IF(Destroyed()) ||
223 NS_WARN_IF(parentNode != element->GetParentNode())) {
224 return 0;
226 if (!positionValue.EqualsLiteral("absolute")) {
227 continue;
229 // ah, we found one, what's its z-index ? If its z-index is auto,
230 // we have to continue climbing the document's tree
231 rv = CSSEditUtils::GetComputedProperty(*element, *nsGkAtoms::z_index,
232 zIndexValue);
233 if (NS_FAILED(rv)) {
234 NS_WARNING(
235 "CSSEditUtils::GetComputedProperty(nsGkAtoms::z_index) failed");
236 return 0;
238 if (NS_WARN_IF(Destroyed()) ||
239 NS_WARN_IF(parentNode != element->GetParentNode())) {
240 return 0;
242 if (!zIndexValue.EqualsLiteral("auto")) {
243 break;
248 if (zIndexValue.EqualsLiteral("auto")) {
249 return 0;
252 nsresult rvIgnored;
253 int32_t result = zIndexValue.ToInteger(&rvIgnored);
254 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
255 "nsAString::ToInteger() failed, but ignored");
256 return result;
259 bool HTMLEditor::CreateGrabberInternal(nsIContent& aParentContent) {
260 if (NS_WARN_IF(mGrabber)) {
261 return false;
264 mGrabber = CreateAnonymousElement(nsGkAtoms::span, aParentContent,
265 u"mozGrabber"_ns, false);
267 // mGrabber may be destroyed during creation due to there may be
268 // mutation event listener.
269 if (!mGrabber) {
270 NS_WARNING(
271 "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozGrabber) "
272 "failed");
273 return false;
276 EventListenerManager* eventListenerManager =
277 mGrabber->GetOrCreateListenerManager();
278 eventListenerManager->AddEventListenerByType(
279 mEventListener, u"mousedown"_ns, TrustedEventsAtSystemGroupBubble());
280 MOZ_ASSERT(mGrabber);
281 return true;
284 NS_IMETHODIMP HTMLEditor::RefreshGrabber() {
285 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
286 return NS_ERROR_FAILURE;
289 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
290 if (NS_WARN_IF(!editActionData.CanHandle())) {
291 return NS_ERROR_NOT_INITIALIZED;
294 nsresult rv = RefreshGrabberInternal();
295 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
296 "HTMLEditor::RefreshGrabberInternal() failed");
297 return EditorBase::ToGenericNSResult(rv);
300 nsresult HTMLEditor::RefreshGrabberInternal() {
301 if (!mAbsolutelyPositionedObject) {
302 return NS_OK;
304 OwningNonNull<Element> absolutelyPositionedObject =
305 *mAbsolutelyPositionedObject;
306 nsresult rv = GetPositionAndDimensions(
307 absolutelyPositionedObject, mPositionedObjectX, mPositionedObjectY,
308 mPositionedObjectWidth, mPositionedObjectHeight,
309 mPositionedObjectBorderLeft, mPositionedObjectBorderTop,
310 mPositionedObjectMarginLeft, mPositionedObjectMarginTop);
311 if (NS_FAILED(rv)) {
312 NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed");
313 return rv;
315 if (NS_WARN_IF(absolutelyPositionedObject != mAbsolutelyPositionedObject)) {
316 return NS_ERROR_FAILURE;
319 RefPtr<nsStyledElement> grabberStyledElement =
320 nsStyledElement::FromNodeOrNull(mGrabber.get());
321 if (!grabberStyledElement) {
322 return NS_OK;
324 rv = SetAnonymousElementPositionWithoutTransaction(
325 *grabberStyledElement, mPositionedObjectX + 12, mPositionedObjectY - 14);
326 if (NS_FAILED(rv)) {
327 NS_WARNING(
328 "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() failed");
329 return rv;
331 if (NS_WARN_IF(grabberStyledElement != mGrabber.get())) {
332 return NS_ERROR_FAILURE;
334 return NS_OK;
337 void HTMLEditor::HideGrabberInternal() {
338 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
339 return;
342 // Move all members to the local variables first since mutation event
343 // listener may try to show grabber while we're hiding them.
344 RefPtr<Element> absolutePositioningObject =
345 std::move(mAbsolutelyPositionedObject);
346 ManualNACPtr grabber = std::move(mGrabber);
347 ManualNACPtr positioningShadow = std::move(mPositioningShadow);
349 // If we're still in dragging mode, it means that the dragging is canceled
350 // by the web app.
351 if (mGrabberClicked || mIsMoving) {
352 mGrabberClicked = false;
353 mIsMoving = false;
354 if (mEventListener) {
355 DebugOnly<nsresult> rvIgnored =
356 static_cast<HTMLEditorEventListener*>(mEventListener.get())
357 ->ListenToMouseMoveEventForGrabber(false);
358 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
359 "HTMLEditorEventListener::"
360 "ListenToMouseMoveEventForGrabber(false) failed");
364 DebugOnly<nsresult> rv = absolutePositioningObject->UnsetAttr(
365 kNameSpaceID_None, nsGkAtoms::_moz_abspos, true);
366 NS_WARNING_ASSERTION(
367 NS_SUCCEEDED(rv),
368 "Element::UnsetAttr(nsGkAtoms::_moz_abspos) failed, but ignored");
370 // We allow the pres shell to be null; when it is, we presume there
371 // are no document observers to notify, but we still want to
372 // UnbindFromTree.
373 RefPtr<PresShell> presShell = GetPresShell();
374 if (grabber) {
375 DeleteRefToAnonymousNode(std::move(grabber), presShell);
377 if (positioningShadow) {
378 DeleteRefToAnonymousNode(std::move(positioningShadow), presShell);
382 nsresult HTMLEditor::ShowGrabberInternal(Element& aElement) {
383 if (NS_WARN_IF(!IsDescendantOfEditorRoot(&aElement))) {
384 return NS_ERROR_UNEXPECTED;
387 if (NS_WARN_IF(mGrabber)) {
388 return NS_ERROR_UNEXPECTED;
391 nsAutoString classValue;
392 nsresult rv =
393 GetTemporaryStyleForFocusedPositionedElement(aElement, classValue);
394 if (NS_FAILED(rv)) {
395 NS_WARNING(
396 "HTMLEditor::GetTemporaryStyleForFocusedPositionedElement() failed");
397 return rv;
400 rv = aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos, classValue,
401 true);
402 if (NS_FAILED(rv)) {
403 NS_WARNING("Element::SetAttr(nsGkAtoms::_moz_abspos) failed");
404 return rv;
407 mAbsolutelyPositionedObject = &aElement;
409 Element* parentElement = aElement.GetParentElement();
410 if (NS_WARN_IF(!parentElement)) {
411 return NS_ERROR_FAILURE;
414 if (!CreateGrabberInternal(*parentElement)) {
415 NS_WARNING("HTMLEditor::CreateGrabberInternal() failed");
416 return NS_ERROR_FAILURE;
419 // If we succeeded to create the grabber, HideGrabberInternal() hasn't been
420 // called yet. So, mAbsolutelyPositionedObject should be non-nullptr.
421 MOZ_ASSERT(mAbsolutelyPositionedObject);
423 // Finally, move the grabber to proper position.
424 rv = RefreshGrabberInternal();
425 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
426 "HTMLEditor::RefereshGrabberInternal() failed");
427 return rv;
430 nsresult HTMLEditor::StartMoving() {
431 MOZ_ASSERT(mGrabber);
433 RefPtr<Element> parentElement = mGrabber->GetParentElement();
434 if (NS_WARN_IF(!parentElement) || NS_WARN_IF(!mAbsolutelyPositionedObject)) {
435 return NS_ERROR_FAILURE;
438 // now, let's create the resizing shadow
439 mPositioningShadow =
440 CreateShadow(*parentElement, *mAbsolutelyPositionedObject);
441 if (!mPositioningShadow) {
442 NS_WARNING("HTMLEditor::CreateShadow() failed");
443 return NS_ERROR_FAILURE;
445 if (!mAbsolutelyPositionedObject) {
446 NS_WARNING("The target has gone during HTMLEditor::CreateShadow()");
447 return NS_ERROR_FAILURE;
449 RefPtr<Element> positioningShadow = mPositioningShadow.get();
450 RefPtr<Element> absolutelyPositionedObject = mAbsolutelyPositionedObject;
451 nsresult rv =
452 SetShadowPosition(*positioningShadow, *absolutelyPositionedObject,
453 mPositionedObjectX, mPositionedObjectY);
454 if (NS_FAILED(rv)) {
455 NS_WARNING("HTMLEditor::SetShadowPosition() failed");
456 return rv;
459 // make the shadow appear
460 DebugOnly<nsresult> rvIgnored =
461 mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
462 NS_WARNING_ASSERTION(
463 NS_SUCCEEDED(rvIgnored),
464 "Element::UnsetAttr(nsGkAtoms::_class) failed, but ignored");
466 // position it
467 if (RefPtr<nsStyledElement> positioningShadowStyledElement =
468 nsStyledElement::FromNode(mPositioningShadow.get())) {
469 nsresult rv;
470 rv = mCSSEditUtils->SetCSSPropertyPixelsWithoutTransaction(
471 *positioningShadowStyledElement, *nsGkAtoms::width,
472 mPositionedObjectWidth);
473 if (rv == NS_ERROR_EDITOR_DESTROYED) {
474 NS_WARNING(
475 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
476 "nsGkAtoms::width) destroyed the editor");
477 return NS_ERROR_EDITOR_DESTROYED;
479 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
480 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
481 "nsGkAtoms::width) failed, but ignored");
482 rv = mCSSEditUtils->SetCSSPropertyPixelsWithoutTransaction(
483 *positioningShadowStyledElement, *nsGkAtoms::height,
484 mPositionedObjectHeight);
485 if (rv == NS_ERROR_EDITOR_DESTROYED) {
486 NS_WARNING(
487 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
488 "nsGkAtoms::height) destroyed the editor");
489 return NS_ERROR_EDITOR_DESTROYED;
491 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
492 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
493 "nsGkAtoms::height) failed, but ignored");
496 mIsMoving = true;
497 return NS_OK; // XXX Looks like nobody refers this result
500 void HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) {
501 if (mSnapToGridEnabled && mGridSize) {
502 newX = (int32_t)floor(((float)newX / (float)mGridSize) + 0.5f) * mGridSize;
503 newY = (int32_t)floor(((float)newY / (float)mGridSize) + 0.5f) * mGridSize;
507 nsresult HTMLEditor::GrabberClicked() {
508 if (NS_WARN_IF(!mEventListener)) {
509 return NS_ERROR_NOT_INITIALIZED;
511 nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
512 ->ListenToMouseMoveEventForGrabber(true);
513 if (NS_FAILED(rv)) {
514 NS_WARNING(
515 "HTMLEditorEventListener::ListenToMouseMoveEventForGrabber(true) "
516 "failed, but ignored");
517 return NS_OK;
519 mGrabberClicked = true;
520 return NS_OK;
523 nsresult HTMLEditor::EndMoving() {
524 if (mPositioningShadow) {
525 RefPtr<PresShell> presShell = GetPresShell();
526 if (NS_WARN_IF(!presShell)) {
527 return NS_ERROR_NOT_INITIALIZED;
530 DeleteRefToAnonymousNode(std::move(mPositioningShadow), presShell);
532 mPositioningShadow = nullptr;
535 if (mEventListener) {
536 DebugOnly<nsresult> rvIgnored =
537 static_cast<HTMLEditorEventListener*>(mEventListener.get())
538 ->ListenToMouseMoveEventForGrabber(false);
539 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
540 "HTMLEditorEventListener::"
541 "ListenToMouseMoveEventForGrabber(false) failed");
544 mGrabberClicked = false;
545 mIsMoving = false;
546 nsresult rv = RefreshEditingUI();
547 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
548 "HTMLEditor::RefreshEditingUI() failed");
549 return rv;
552 nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) {
553 nsresult rv = EndMoving();
554 if (NS_FAILED(rv)) {
555 NS_WARNING("HTMLEditor::EndMoving() failed");
556 return rv;
559 // we have now to set the new width and height of the resized object
560 // we don't set the x and y position because we don't control that in
561 // a normal HTML layout
562 int32_t newX = mPositionedObjectX + aX - mOriginalX -
563 (mPositionedObjectBorderLeft + mPositionedObjectMarginLeft);
564 int32_t newY = mPositionedObjectY + aY - mOriginalY -
565 (mPositionedObjectBorderTop + mPositionedObjectMarginTop);
567 SnapToGrid(newX, newY);
569 nsAutoString x, y;
570 x.AppendInt(newX);
571 y.AppendInt(newY);
573 // we want one transaction only from a user's point of view
574 AutoPlaceholderBatch treatAsOneTransaction(*this,
575 ScrollSelectionIntoView::Yes);
577 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
578 return NS_ERROR_FAILURE;
580 if (RefPtr<nsStyledElement> styledAbsolutelyPositionedElement =
581 nsStyledElement::FromNode(mAbsolutelyPositionedObject)) {
582 nsresult rv;
583 rv = mCSSEditUtils->SetCSSPropertyPixelsWithTransaction(
584 *styledAbsolutelyPositionedElement, *nsGkAtoms::top, newY);
585 if (rv == NS_ERROR_EDITOR_DESTROYED) {
586 NS_WARNING(
587 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
588 "destroyed the editor");
589 return NS_ERROR_EDITOR_DESTROYED;
591 NS_WARNING_ASSERTION(
592 NS_SUCCEEDED(rv),
593 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
594 "failed, but ignored");
595 rv = mCSSEditUtils->SetCSSPropertyPixelsWithTransaction(
596 *styledAbsolutelyPositionedElement, *nsGkAtoms::left, newX);
597 if (rv == NS_ERROR_EDITOR_DESTROYED) {
598 NS_WARNING(
599 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
600 "destroyed the editor");
601 return NS_ERROR_EDITOR_DESTROYED;
603 NS_WARNING_ASSERTION(
604 NS_SUCCEEDED(rv),
605 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
606 "failed, but ignored");
608 // keep track of that size
609 mPositionedObjectX = newX;
610 mPositionedObjectY = newY;
612 rv = RefreshResizers();
613 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
614 "HTMLEditor::RefreshResizers() failed");
615 return rv;
618 void HTMLEditor::AddPositioningOffset(int32_t& aX, int32_t& aY) {
619 // Get the positioning offset
620 int32_t positioningOffset =
621 Preferences::GetInt("editor.positioning.offset", 0);
623 aX += positioningOffset;
624 aY += positioningOffset;
627 nsresult HTMLEditor::SetPositionToAbsoluteOrStatic(Element& aElement,
628 bool aEnabled) {
629 nsAutoString positionValue;
630 DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetComputedProperty(
631 aElement, *nsGkAtoms::position, positionValue);
632 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
633 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) "
634 "failed, but ignored");
635 // nothing to do if the element is already in the state we want
636 if (positionValue.EqualsLiteral("absolute") == aEnabled) {
637 return NS_OK;
640 if (aEnabled) {
641 nsresult rv = SetPositionToAbsolute(aElement);
642 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
643 "HTMLEditor::SetPositionToAbsolute() failed");
644 return rv;
647 nsresult rv = SetPositionToStatic(aElement);
648 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
649 "HTMLEditor::SetPositionToStatic() failed");
650 return rv;
653 nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
654 MOZ_ASSERT(IsEditActionDataAvailable());
656 AutoPlaceholderBatch treatAsOneTransaction(*this,
657 ScrollSelectionIntoView::Yes);
659 int32_t x, y;
660 DebugOnly<nsresult> rvIgnored = GetElementOrigin(aElement, x, y);
661 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
662 "HTMLEditor::GetElementOrigin() failed, but ignored");
664 nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
665 if (styledElement) {
666 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
667 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
668 nsresult rv = mCSSEditUtils->SetCSSPropertyWithTransaction(
669 MOZ_KnownLive(*styledElement), *nsGkAtoms::position, u"absolute"_ns);
670 if (rv == NS_ERROR_EDITOR_DESTROYED) {
671 NS_WARNING(
672 "CSSEditUtils::SetCSSProperyWithTransaction(nsGkAtoms::Position) "
673 "destroyed the editor");
674 return NS_ERROR_EDITOR_DESTROYED;
676 NS_WARNING_ASSERTION(
677 NS_SUCCEEDED(rvIgnored),
678 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::position, "
679 "absolute) failed, but ignored");
682 AddPositioningOffset(x, y);
683 SnapToGrid(x, y);
684 if (styledElement) {
685 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
686 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
687 nsresult rv =
688 SetTopAndLeftWithTransaction(MOZ_KnownLive(*styledElement), x, y);
689 if (NS_FAILED(rv)) {
690 NS_WARNING("HTMLEditor::SetTopAndLeftWithTransaction() failed");
691 return rv;
695 // we may need to create a br if the positioned element is alone in its
696 // container
697 nsINode* parentNode = aElement.GetParentNode();
698 if (parentNode->GetChildCount() != 1) {
699 return NS_OK;
701 Result<RefPtr<Element>, nsresult> resultOfInsertingBRElement =
702 InsertBRElementWithTransaction(EditorDOMPoint(parentNode, 0));
703 if (resultOfInsertingBRElement.isErr()) {
704 NS_WARNING("HTMLEditor::InsertBRElementWithTransaction() failed");
705 return resultOfInsertingBRElement.unwrapErr();
707 MOZ_ASSERT(resultOfInsertingBRElement.inspect());
708 return NS_OK;
711 nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
712 nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
713 if (NS_WARN_IF(!styledElement)) {
714 return NS_ERROR_INVALID_ARG;
717 AutoPlaceholderBatch treatAsOneTransaction(*this,
718 ScrollSelectionIntoView::Yes);
720 nsresult rv;
721 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
722 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
723 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
724 MOZ_KnownLive(*styledElement), *nsGkAtoms::position, u""_ns);
725 if (rv == NS_ERROR_EDITOR_DESTROYED) {
726 NS_WARNING(
727 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
728 "destroyed the editor");
729 return NS_ERROR_EDITOR_DESTROYED;
731 NS_WARNING_ASSERTION(
732 NS_SUCCEEDED(rv),
733 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
734 "failed, but ignored");
735 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
736 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
737 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
738 MOZ_KnownLive(*styledElement), *nsGkAtoms::top, u""_ns);
739 if (rv == NS_ERROR_EDITOR_DESTROYED) {
740 NS_WARNING(
741 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
742 "destroyed the editor");
743 return NS_ERROR_EDITOR_DESTROYED;
745 NS_WARNING_ASSERTION(
746 NS_SUCCEEDED(rv),
747 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
748 "failed, but ignored");
749 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
750 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
751 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
752 MOZ_KnownLive(*styledElement), *nsGkAtoms::left, u""_ns);
753 if (rv == NS_ERROR_EDITOR_DESTROYED) {
754 NS_WARNING(
755 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
756 "destroyed the editor");
757 return NS_ERROR_EDITOR_DESTROYED;
759 NS_WARNING_ASSERTION(
760 NS_SUCCEEDED(rv),
761 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
762 "failed, but ignored");
763 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
764 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
765 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
766 MOZ_KnownLive(*styledElement), *nsGkAtoms::z_index, u""_ns);
767 if (rv == NS_ERROR_EDITOR_DESTROYED) {
768 NS_WARNING(
769 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
770 "destroyed the editor");
771 return NS_ERROR_EDITOR_DESTROYED;
773 NS_WARNING_ASSERTION(
774 NS_SUCCEEDED(rv),
775 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
776 "failed, but ignored");
778 if (!HTMLEditUtils::IsImage(styledElement)) {
779 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
780 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
781 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
782 MOZ_KnownLive(*styledElement), *nsGkAtoms::width, u""_ns);
783 if (rv == NS_ERROR_EDITOR_DESTROYED) {
784 NS_WARNING(
785 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
786 "destroyed the editor");
787 return NS_ERROR_EDITOR_DESTROYED;
789 NS_WARNING_ASSERTION(
790 NS_SUCCEEDED(rv),
791 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
792 "failed, but ignored");
793 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
794 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
795 rv = mCSSEditUtils->RemoveCSSPropertyWithTransaction(
796 MOZ_KnownLive(*styledElement), *nsGkAtoms::height, u""_ns);
797 if (rv == NS_ERROR_EDITOR_DESTROYED) {
798 NS_WARNING(
799 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
800 "destroyed the editor");
801 return NS_ERROR_EDITOR_DESTROYED;
803 NS_WARNING_ASSERTION(
804 NS_SUCCEEDED(rv),
805 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
806 "failed, but ignored");
809 if (!styledElement->IsHTMLElement(nsGkAtoms::div) ||
810 HTMLEditor::HasStyleOrIdOrClassAttribute(*styledElement)) {
811 return NS_OK;
814 // Make sure the first fild and last child of aElement starts/ends hard
815 // line(s) even after removing `aElement`.
816 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
817 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
818 rv = EnsureHardLineBeginsWithFirstChildOf(MOZ_KnownLive(*styledElement));
819 if (NS_FAILED(rv)) {
820 NS_WARNING("HTMLEditor::EnsureHardLineBeginsWithFirstChildOf() failed");
821 return rv;
823 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
824 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
825 rv = EnsureHardLineEndsWithLastChildOf(MOZ_KnownLive(*styledElement));
826 if (NS_FAILED(rv)) {
827 NS_WARNING("HTMLEditor::EnsureHardLineEndsWithLastChildOf() failed");
828 return rv;
830 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
831 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
832 rv = RemoveContainerWithTransaction(MOZ_KnownLive(*styledElement));
833 if (NS_WARN_IF(Destroyed())) {
834 return NS_ERROR_EDITOR_DESTROYED;
836 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
837 "HTMLEditor::RemoveContainerWithTransaction() failed");
838 return rv;
841 NS_IMETHODIMP HTMLEditor::SetSnapToGridEnabled(bool aEnabled) {
842 mSnapToGridEnabled = aEnabled;
843 return NS_OK;
846 NS_IMETHODIMP HTMLEditor::GetSnapToGridEnabled(bool* aIsEnabled) {
847 *aIsEnabled = mSnapToGridEnabled;
848 return NS_OK;
851 NS_IMETHODIMP HTMLEditor::SetGridSize(uint32_t aSize) {
852 mGridSize = aSize;
853 return NS_OK;
856 NS_IMETHODIMP HTMLEditor::GetGridSize(uint32_t* aSize) {
857 *aSize = mGridSize;
858 return NS_OK;
861 nsresult HTMLEditor::SetTopAndLeftWithTransaction(
862 nsStyledElement& aStyledElement, int32_t aX, int32_t aY) {
863 AutoPlaceholderBatch treatAsOneTransaction(*this,
864 ScrollSelectionIntoView::Yes);
865 nsresult rv;
866 rv = mCSSEditUtils->SetCSSPropertyPixelsWithTransaction(aStyledElement,
867 *nsGkAtoms::left, aX);
868 if (rv == NS_ERROR_EDITOR_DESTROYED) {
869 NS_WARNING(
870 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
871 "destroyed the editor");
872 return NS_ERROR_EDITOR_DESTROYED;
874 NS_WARNING_ASSERTION(
875 NS_SUCCEEDED(rv),
876 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
877 "failed, but ignored");
878 rv = mCSSEditUtils->SetCSSPropertyPixelsWithTransaction(aStyledElement,
879 *nsGkAtoms::top, aY);
880 if (rv == NS_ERROR_EDITOR_DESTROYED) {
881 NS_WARNING(
882 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
883 "destroyed the editor");
884 return NS_ERROR_EDITOR_DESTROYED;
886 NS_WARNING_ASSERTION(
887 NS_SUCCEEDED(rv),
888 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
889 "failed, but ignored");
890 return NS_OK;
893 nsresult HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(
894 Element& aElement, nsAString& aReturn) {
895 // we are going to outline the positioned element and bring it to the
896 // front to overlap any other element intersecting with it. But
897 // first, let's see what's the background and foreground colors of the
898 // positioned element.
899 // if background-image computed value is 'none,
900 // If the background color is 'auto' and R G B values of the foreground are
901 // each above #d0, use a black background
902 // If the background color is 'auto' and at least one of R G B values of
903 // the foreground is below #d0, use a white background
904 // Otherwise don't change background/foreground
905 aReturn.Truncate();
907 nsAutoString backgroundImageValue;
908 nsresult rv = CSSEditUtils::GetComputedProperty(
909 aElement, *nsGkAtoms::background_image, backgroundImageValue);
910 if (NS_FAILED(rv)) {
911 NS_WARNING(
912 "CSSEditUtils::GetComputedProperty(nsGkAtoms::background_image) "
913 "failed");
914 return rv;
916 if (!backgroundImageValue.EqualsLiteral("none")) {
917 return NS_OK;
920 nsAutoString backgroundColorValue;
921 rv = CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::backgroundColor,
922 backgroundColorValue);
923 if (NS_FAILED(rv)) {
924 NS_WARNING(
925 "CSSEditUtils::GetComputedProperty(nsGkAtoms::backgroundColor) "
926 "failed");
927 return rv;
929 if (!backgroundColorValue.EqualsLiteral("rgba(0, 0, 0, 0)")) {
930 return NS_OK;
933 RefPtr<ComputedStyle> style =
934 nsComputedDOMStyle::GetComputedStyle(&aElement, nullptr);
935 if (NS_WARN_IF(Destroyed())) {
936 return NS_ERROR_EDITOR_DESTROYED;
938 if (!style) {
939 NS_WARNING("nsComputedDOMStyle::GetComputedStyle() failed");
940 return NS_ERROR_FAILURE;
943 static const uint8_t kBlackBgTrigger = 0xd0;
945 const auto& color = style->StyleText()->mColor;
946 if (color.red >= kBlackBgTrigger && color.green >= kBlackBgTrigger &&
947 color.blue >= kBlackBgTrigger) {
948 aReturn.AssignLiteral("black");
949 } else {
950 aReturn.AssignLiteral("white");
953 return NS_OK;
956 } // namespace mozilla