Bug 1890689 apply drift correction to input rate instead of output rate r=pehrsons
[gecko.git] / editor / libeditor / HTMLAbsPositionEditor.cpp
blob2378d243dd1e9a39eec68acb330c5dcb2ecebffa
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 "HTMLEditHelpers.h"
12 #include "HTMLEditorEventListener.h"
13 #include "HTMLEditUtils.h"
15 #include "mozilla/EventListenerManager.h"
16 #include "mozilla/mozalloc.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/PresShell.h"
19 #include "mozilla/StaticPrefs_editor.h"
20 #include "mozilla/dom/AncestorIterator.h"
21 #include "mozilla/dom/Selection.h"
22 #include "mozilla/dom/Element.h"
23 #include "mozilla/dom/EventTarget.h"
24 #include "nsAString.h"
25 #include "nsAlgorithm.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 Result<int32_t, nsresult> HTMLEditor::AddZIndexWithTransaction(
139 nsStyledElement& aStyledElement, int32_t aChange) {
140 if (!aChange) {
141 return 0; // XXX Why don't we return current z-index value in this case?
144 int32_t zIndex = GetZIndex(aStyledElement);
145 if (NS_WARN_IF(Destroyed())) {
146 return Err(NS_ERROR_EDITOR_DESTROYED);
148 zIndex = std::max(zIndex + aChange, 0);
149 nsresult rv = SetZIndexWithTransaction(aStyledElement, zIndex);
150 if (rv == NS_ERROR_EDITOR_DESTROYED) {
151 NS_WARNING("HTMLEditor::SetZIndexWithTransaction() destroyed the editor");
152 return Err(NS_ERROR_EDITOR_DESTROYED);
154 NS_WARNING_ASSERTION(
155 NS_SUCCEEDED(rv),
156 "HTMLEditor::SetZIndexWithTransaction() failed, but ignored");
157 return zIndex;
160 nsresult HTMLEditor::SetZIndexWithTransaction(nsStyledElement& aStyledElement,
161 int32_t aZIndex) {
162 nsAutoString zIndexValue;
163 zIndexValue.AppendInt(aZIndex);
165 nsresult rv = CSSEditUtils::SetCSSPropertyWithTransaction(
166 *this, aStyledElement, *nsGkAtoms::z_index, zIndexValue);
167 if (rv == NS_ERROR_EDITOR_DESTROYED) {
168 NS_WARNING(
169 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::z_index) "
170 "destroyed the editor");
171 return NS_ERROR_EDITOR_DESTROYED;
173 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
174 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::"
175 "z_index) failed, but ignored");
176 return NS_OK;
179 nsresult HTMLEditor::AddZIndexAsAction(int32_t aChange,
180 nsIPrincipal* aPrincipal) {
181 MOZ_ASSERT(IsEditActionDataAvailable());
183 AutoEditActionDataSetter editActionData(
184 *this, EditAction::eIncreaseOrDecreaseZIndex, aPrincipal);
185 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
186 if (NS_FAILED(rv)) {
187 NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
188 "CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
189 return EditorBase::ToGenericNSResult(rv);
192 Result<EditActionResult, nsresult> result = AddZIndexAsSubAction(aChange);
193 if (MOZ_UNLIKELY(result.isErr())) {
194 NS_WARNING("HTMLEditor::AddZIndexAsSubAction() failed");
195 return EditorBase::ToGenericNSResult(result.unwrapErr());
197 return NS_OK;
200 int32_t HTMLEditor::GetZIndex(Element& aElement) {
201 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
202 if (NS_WARN_IF(!editActionData.CanHandle())) {
203 return 0;
206 nsAutoString zIndexValue;
208 nsresult rv = CSSEditUtils::GetSpecifiedProperty(
209 aElement, *nsGkAtoms::z_index, zIndexValue);
210 if (NS_FAILED(rv)) {
211 NS_WARNING("CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::z_index) failed");
212 return 0;
214 if (zIndexValue.EqualsLiteral("auto")) {
215 if (!aElement.GetParentElement()) {
216 NS_WARNING("aElement was an orphan node or the root node");
217 return 0;
219 // we have to look at the positioned ancestors
220 // cf. CSS 2 spec section 9.9.1
221 nsAutoString positionValue;
222 for (RefPtr<Element> element = aElement.GetParentElement(); element;
223 element = element->GetParentElement()) {
224 if (element->IsHTMLElement(nsGkAtoms::body)) {
225 return 0;
227 nsCOMPtr<nsINode> parentNode = element->GetParentElement();
228 nsresult rv = CSSEditUtils::GetComputedProperty(
229 *element, *nsGkAtoms::position, positionValue);
230 if (NS_FAILED(rv)) {
231 NS_WARNING(
232 "CSSEditUtils::GetComputedProperty(nsGkAtoms::position) failed");
233 return 0;
235 if (NS_WARN_IF(Destroyed()) ||
236 NS_WARN_IF(parentNode != element->GetParentNode())) {
237 return 0;
239 if (!positionValue.EqualsLiteral("absolute")) {
240 continue;
242 // ah, we found one, what's its z-index ? If its z-index is auto,
243 // we have to continue climbing the document's tree
244 rv = CSSEditUtils::GetComputedProperty(*element, *nsGkAtoms::z_index,
245 zIndexValue);
246 if (NS_FAILED(rv)) {
247 NS_WARNING(
248 "CSSEditUtils::GetComputedProperty(nsGkAtoms::z_index) failed");
249 return 0;
251 if (NS_WARN_IF(Destroyed()) ||
252 NS_WARN_IF(parentNode != element->GetParentNode())) {
253 return 0;
255 if (!zIndexValue.EqualsLiteral("auto")) {
256 break;
261 if (zIndexValue.EqualsLiteral("auto")) {
262 return 0;
265 nsresult rvIgnored;
266 int32_t result = zIndexValue.ToInteger(&rvIgnored);
267 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
268 "nsAString::ToInteger() failed, but ignored");
269 return result;
272 bool HTMLEditor::CreateGrabberInternal(nsIContent& aParentContent) {
273 if (NS_WARN_IF(mGrabber)) {
274 return false;
277 mGrabber = CreateAnonymousElement(nsGkAtoms::span, aParentContent,
278 u"mozGrabber"_ns, false);
280 // mGrabber may be destroyed during creation due to there may be
281 // mutation event listener.
282 if (!mGrabber) {
283 NS_WARNING(
284 "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozGrabber) "
285 "failed");
286 return false;
289 EventListenerManager* eventListenerManager =
290 mGrabber->GetOrCreateListenerManager();
291 eventListenerManager->AddEventListenerByType(
292 mEventListener, u"mousedown"_ns, TrustedEventsAtSystemGroupBubble());
293 MOZ_ASSERT(mGrabber);
294 return true;
297 nsresult HTMLEditor::RefreshGrabberInternal() {
298 MOZ_ASSERT(IsEditActionDataAvailable());
300 if (!mAbsolutelyPositionedObject) {
301 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_WARN_IF(Destroyed())) {
327 return NS_ERROR_EDITOR_DESTROYED;
329 if (NS_FAILED(rv)) {
330 NS_WARNING(
331 "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() failed");
332 return rv;
334 if (NS_WARN_IF(grabberStyledElement != mGrabber.get())) {
335 return NS_ERROR_FAILURE;
337 return NS_OK;
340 void HTMLEditor::HideGrabberInternal() {
341 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
342 return;
345 // Move all members to the local variables first since mutation event
346 // listener may try to show grabber while we're hiding them.
347 RefPtr<Element> absolutePositioningObject =
348 std::move(mAbsolutelyPositionedObject);
349 ManualNACPtr grabber = std::move(mGrabber);
350 ManualNACPtr positioningShadow = std::move(mPositioningShadow);
352 // If we're still in dragging mode, it means that the dragging is canceled
353 // by the web app.
354 if (mGrabberClicked || mIsMoving) {
355 mGrabberClicked = false;
356 mIsMoving = false;
357 if (mEventListener) {
358 DebugOnly<nsresult> rvIgnored =
359 static_cast<HTMLEditorEventListener*>(mEventListener.get())
360 ->ListenToMouseMoveEventForGrabber(false);
361 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
362 "HTMLEditorEventListener::"
363 "ListenToMouseMoveEventForGrabber(false) failed");
367 DebugOnly<nsresult> rv = absolutePositioningObject->UnsetAttr(
368 kNameSpaceID_None, nsGkAtoms::_moz_abspos, true);
369 NS_WARNING_ASSERTION(
370 NS_SUCCEEDED(rv),
371 "Element::UnsetAttr(nsGkAtoms::_moz_abspos) failed, but ignored");
373 // We allow the pres shell to be null; when it is, we presume there
374 // are no document observers to notify, but we still want to
375 // UnbindFromTree.
376 RefPtr<PresShell> presShell = GetPresShell();
377 if (grabber) {
378 DeleteRefToAnonymousNode(std::move(grabber), presShell);
380 if (positioningShadow) {
381 DeleteRefToAnonymousNode(std::move(positioningShadow), presShell);
385 nsresult HTMLEditor::ShowGrabberInternal(Element& aElement) {
386 MOZ_ASSERT(IsEditActionDataAvailable());
388 const RefPtr<Element> editingHost = ComputeEditingHost();
389 if (NS_WARN_IF(!editingHost) ||
390 NS_WARN_IF(!aElement.IsInclusiveDescendantOf(editingHost))) {
391 return NS_ERROR_UNEXPECTED;
394 if (NS_WARN_IF(mGrabber)) {
395 return NS_ERROR_UNEXPECTED;
398 nsAutoString classValue;
399 nsresult rv =
400 GetTemporaryStyleForFocusedPositionedElement(aElement, classValue);
401 if (NS_FAILED(rv)) {
402 NS_WARNING(
403 "HTMLEditor::GetTemporaryStyleForFocusedPositionedElement() failed");
404 return rv;
407 rv = aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos, classValue,
408 true);
409 if (NS_FAILED(rv)) {
410 NS_WARNING("Element::SetAttr(nsGkAtoms::_moz_abspos) failed");
411 return rv;
414 mAbsolutelyPositionedObject = &aElement;
416 Element* parentElement = aElement.GetParentElement();
417 if (NS_WARN_IF(!parentElement)) {
418 return NS_ERROR_FAILURE;
421 if (!CreateGrabberInternal(*parentElement)) {
422 NS_WARNING("HTMLEditor::CreateGrabberInternal() failed");
423 return NS_ERROR_FAILURE;
426 // If we succeeded to create the grabber, HideGrabberInternal() hasn't been
427 // called yet. So, mAbsolutelyPositionedObject should be non-nullptr.
428 MOZ_ASSERT(mAbsolutelyPositionedObject);
430 // Finally, move the grabber to proper position.
431 rv = RefreshGrabberInternal();
432 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
433 "HTMLEditor::RefereshGrabberInternal() failed");
434 return rv;
437 nsresult HTMLEditor::StartMoving() {
438 MOZ_ASSERT(mGrabber);
440 RefPtr<Element> parentElement = mGrabber->GetParentElement();
441 if (NS_WARN_IF(!parentElement) || NS_WARN_IF(!mAbsolutelyPositionedObject)) {
442 return NS_ERROR_FAILURE;
445 // now, let's create the resizing shadow
446 mPositioningShadow =
447 CreateShadow(*parentElement, *mAbsolutelyPositionedObject);
448 if (!mPositioningShadow) {
449 NS_WARNING("HTMLEditor::CreateShadow() failed");
450 return NS_ERROR_FAILURE;
452 if (!mAbsolutelyPositionedObject) {
453 NS_WARNING("The target has gone during HTMLEditor::CreateShadow()");
454 return NS_ERROR_FAILURE;
456 RefPtr<Element> positioningShadow = mPositioningShadow.get();
457 RefPtr<Element> absolutelyPositionedObject = mAbsolutelyPositionedObject;
458 nsresult rv =
459 SetShadowPosition(*positioningShadow, *absolutelyPositionedObject,
460 mPositionedObjectX, mPositionedObjectY);
461 if (NS_FAILED(rv)) {
462 NS_WARNING("HTMLEditor::SetShadowPosition() failed");
463 return rv;
466 // make the shadow appear
467 DebugOnly<nsresult> rvIgnored =
468 mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true);
469 NS_WARNING_ASSERTION(
470 NS_SUCCEEDED(rvIgnored),
471 "Element::UnsetAttr(nsGkAtoms::_class) failed, but ignored");
473 // position it
474 if (RefPtr<nsStyledElement> positioningShadowStyledElement =
475 nsStyledElement::FromNode(mPositioningShadow.get())) {
476 nsresult rv;
477 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
478 *positioningShadowStyledElement, *nsGkAtoms::width,
479 mPositionedObjectWidth);
480 if (rv == NS_ERROR_EDITOR_DESTROYED) {
481 NS_WARNING(
482 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
483 "nsGkAtoms::width) destroyed the editor");
484 return NS_ERROR_EDITOR_DESTROYED;
486 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
487 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
488 "nsGkAtoms::width) failed, but ignored");
489 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(
490 *positioningShadowStyledElement, *nsGkAtoms::height,
491 mPositionedObjectHeight);
492 if (rv == NS_ERROR_EDITOR_DESTROYED) {
493 NS_WARNING(
494 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
495 "nsGkAtoms::height) destroyed the editor");
496 return NS_ERROR_EDITOR_DESTROYED;
498 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
499 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction("
500 "nsGkAtoms::height) failed, but ignored");
503 mIsMoving = true;
504 return NS_OK; // XXX Looks like nobody refers this result
507 void HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) const {
508 if (mSnapToGridEnabled && mGridSize) {
509 newX = (int32_t)floor(((float)newX / (float)mGridSize) + 0.5f) * mGridSize;
510 newY = (int32_t)floor(((float)newY / (float)mGridSize) + 0.5f) * mGridSize;
514 nsresult HTMLEditor::GrabberClicked() {
515 if (NS_WARN_IF(!mEventListener)) {
516 return NS_ERROR_NOT_INITIALIZED;
518 nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get())
519 ->ListenToMouseMoveEventForGrabber(true);
520 if (NS_FAILED(rv)) {
521 NS_WARNING(
522 "HTMLEditorEventListener::ListenToMouseMoveEventForGrabber(true) "
523 "failed, but ignored");
524 return NS_OK;
526 mGrabberClicked = true;
527 return NS_OK;
530 nsresult HTMLEditor::EndMoving() {
531 if (mPositioningShadow) {
532 RefPtr<PresShell> presShell = GetPresShell();
533 if (NS_WARN_IF(!presShell)) {
534 return NS_ERROR_NOT_INITIALIZED;
537 DeleteRefToAnonymousNode(std::move(mPositioningShadow), presShell);
539 mPositioningShadow = nullptr;
542 if (mEventListener) {
543 DebugOnly<nsresult> rvIgnored =
544 static_cast<HTMLEditorEventListener*>(mEventListener.get())
545 ->ListenToMouseMoveEventForGrabber(false);
546 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
547 "HTMLEditorEventListener::"
548 "ListenToMouseMoveEventForGrabber(false) failed");
551 mGrabberClicked = false;
552 mIsMoving = false;
553 nsresult rv = RefreshEditingUI();
554 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
555 "HTMLEditor::RefreshEditingUI() failed");
556 return rv;
559 nsresult HTMLEditor::SetFinalPosition(int32_t aX, int32_t aY) {
560 MOZ_ASSERT(IsEditActionDataAvailable());
562 nsresult rv = EndMoving();
563 if (NS_FAILED(rv)) {
564 NS_WARNING("HTMLEditor::EndMoving() failed");
565 return rv;
568 // we have now to set the new width and height of the resized object
569 // we don't set the x and y position because we don't control that in
570 // a normal HTML layout
571 int32_t newX = mPositionedObjectX + aX - mOriginalX -
572 (mPositionedObjectBorderLeft + mPositionedObjectMarginLeft);
573 int32_t newY = mPositionedObjectY + aY - mOriginalY -
574 (mPositionedObjectBorderTop + mPositionedObjectMarginTop);
576 SnapToGrid(newX, newY);
578 nsAutoString x, y;
579 x.AppendInt(newX);
580 y.AppendInt(newY);
582 // we want one transaction only from a user's point of view
583 AutoPlaceholderBatch treatAsOneTransaction(
584 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
586 if (NS_WARN_IF(!mAbsolutelyPositionedObject)) {
587 return NS_ERROR_FAILURE;
589 if (RefPtr<nsStyledElement> styledAbsolutelyPositionedElement =
590 nsStyledElement::FromNode(mAbsolutelyPositionedObject)) {
591 nsresult rv;
592 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
593 *this, *styledAbsolutelyPositionedElement, *nsGkAtoms::top, newY);
594 if (rv == NS_ERROR_EDITOR_DESTROYED) {
595 NS_WARNING(
596 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
597 "destroyed the editor");
598 return NS_ERROR_EDITOR_DESTROYED;
600 NS_WARNING_ASSERTION(
601 NS_SUCCEEDED(rv),
602 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
603 "failed, but ignored");
604 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(
605 *this, *styledAbsolutelyPositionedElement, *nsGkAtoms::left, newX);
606 if (rv == NS_ERROR_EDITOR_DESTROYED) {
607 NS_WARNING(
608 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
609 "destroyed the editor");
610 return NS_ERROR_EDITOR_DESTROYED;
612 NS_WARNING_ASSERTION(
613 NS_SUCCEEDED(rv),
614 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
615 "failed, but ignored");
617 // keep track of that size
618 mPositionedObjectX = newX;
619 mPositionedObjectY = newY;
621 rv = RefreshResizersInternal();
622 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
623 "HTMLEditor::RefreshResizersInternal() failed");
624 return rv;
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(
657 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
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 = CSSEditUtils::SetCSSPropertyWithTransaction(
669 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position,
670 u"absolute"_ns);
671 if (rv == NS_ERROR_EDITOR_DESTROYED) {
672 NS_WARNING(
673 "CSSEditUtils::SetCSSProperyWithTransaction(nsGkAtoms::Position) "
674 "destroyed the editor");
675 return NS_ERROR_EDITOR_DESTROYED;
677 NS_WARNING_ASSERTION(
678 NS_SUCCEEDED(rvIgnored),
679 "CSSEditUtils::SetCSSPropertyWithTransaction(nsGkAtoms::position, "
680 "absolute) failed, but ignored");
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<CreateElementResult, nsresult> insertBRElementResult =
702 InsertBRElement(WithTransaction::Yes, EditorDOMPoint(parentNode, 0u));
703 if (MOZ_UNLIKELY(insertBRElementResult.isErr())) {
704 NS_WARNING("HTMLEditor::InsertBRElement(WithTransaction::Yes) failed");
705 return insertBRElementResult.unwrapErr();
707 // XXX Is this intentional selection change?
708 nsresult rv = insertBRElementResult.inspect().SuggestCaretPointTo(
709 *this, {SuggestCaret::OnlyIfHasSuggestion,
710 SuggestCaret::OnlyIfTransactionsAllowedToDoIt});
711 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
712 "CreateElementResult::SuggestCaretPointTo() failed");
713 MOZ_ASSERT(insertBRElementResult.inspect().GetNewNode());
714 return rv;
717 nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
718 nsStyledElement* styledElement = nsStyledElement::FromNode(&aElement);
719 if (NS_WARN_IF(!styledElement)) {
720 return NS_ERROR_INVALID_ARG;
723 AutoPlaceholderBatch treatAsOneTransaction(
724 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
726 nsresult rv;
727 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
728 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
729 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
730 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::position, u""_ns);
731 if (rv == NS_ERROR_EDITOR_DESTROYED) {
732 NS_WARNING(
733 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
734 "destroyed the editor");
735 return NS_ERROR_EDITOR_DESTROYED;
737 NS_WARNING_ASSERTION(
738 NS_SUCCEEDED(rv),
739 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::position) "
740 "failed, but ignored");
741 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
742 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
743 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
744 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::top, u""_ns);
745 if (rv == NS_ERROR_EDITOR_DESTROYED) {
746 NS_WARNING(
747 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
748 "destroyed the editor");
749 return NS_ERROR_EDITOR_DESTROYED;
751 NS_WARNING_ASSERTION(
752 NS_SUCCEEDED(rv),
753 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::top) "
754 "failed, but ignored");
755 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
756 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
757 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
758 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::left, u""_ns);
759 if (rv == NS_ERROR_EDITOR_DESTROYED) {
760 NS_WARNING(
761 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
762 "destroyed the editor");
763 return NS_ERROR_EDITOR_DESTROYED;
765 NS_WARNING_ASSERTION(
766 NS_SUCCEEDED(rv),
767 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::left) "
768 "failed, but ignored");
769 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
770 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
771 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
772 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::z_index, u""_ns);
773 if (rv == NS_ERROR_EDITOR_DESTROYED) {
774 NS_WARNING(
775 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
776 "destroyed the editor");
777 return NS_ERROR_EDITOR_DESTROYED;
779 NS_WARNING_ASSERTION(
780 NS_SUCCEEDED(rv),
781 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::z_index) "
782 "failed, but ignored");
784 if (!HTMLEditUtils::IsImage(styledElement)) {
785 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
786 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
787 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
788 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::width, u""_ns);
789 if (rv == NS_ERROR_EDITOR_DESTROYED) {
790 NS_WARNING(
791 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
792 "destroyed the editor");
793 return NS_ERROR_EDITOR_DESTROYED;
795 NS_WARNING_ASSERTION(
796 NS_SUCCEEDED(rv),
797 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) "
798 "failed, but ignored");
799 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
800 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
801 rv = CSSEditUtils::RemoveCSSPropertyWithTransaction(
802 *this, MOZ_KnownLive(*styledElement), *nsGkAtoms::height, u""_ns);
803 if (rv == NS_ERROR_EDITOR_DESTROYED) {
804 NS_WARNING(
805 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
806 "destroyed the editor");
807 return NS_ERROR_EDITOR_DESTROYED;
809 NS_WARNING_ASSERTION(
810 NS_SUCCEEDED(rv),
811 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) "
812 "failed, but ignored");
815 if (!styledElement->IsHTMLElement(nsGkAtoms::div) ||
816 HTMLEditor::HasStyleOrIdOrClassAttribute(*styledElement)) {
817 return NS_OK;
820 EditorDOMPoint pointToPutCaret;
821 // Make sure the first fild and last child of aElement starts/ends hard
822 // line(s) even after removing `aElement`.
824 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
825 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
826 Result<CreateElementResult, nsresult>
827 maybeInsertBRElementBeforeFirstChildResult =
828 EnsureHardLineBeginsWithFirstChildOf(MOZ_KnownLive(*styledElement));
829 if (MOZ_UNLIKELY(maybeInsertBRElementBeforeFirstChildResult.isErr())) {
830 NS_WARNING("HTMLEditor::EnsureHardLineBeginsWithFirstChildOf() failed");
831 return maybeInsertBRElementBeforeFirstChildResult.unwrapErr();
833 CreateElementResult unwrappedResult =
834 maybeInsertBRElementBeforeFirstChildResult.unwrap();
835 if (unwrappedResult.HasCaretPointSuggestion()) {
836 pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
840 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
841 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
842 Result<CreateElementResult, nsresult>
843 maybeInsertBRElementAfterLastChildResult =
844 EnsureHardLineEndsWithLastChildOf(MOZ_KnownLive(*styledElement));
845 if (MOZ_UNLIKELY(maybeInsertBRElementAfterLastChildResult.isErr())) {
846 NS_WARNING("HTMLEditor::EnsureHardLineEndsWithLastChildOf() failed");
847 return maybeInsertBRElementAfterLastChildResult.unwrapErr();
849 CreateElementResult unwrappedResult =
850 maybeInsertBRElementAfterLastChildResult.unwrap();
851 if (unwrappedResult.HasCaretPointSuggestion()) {
852 pointToPutCaret = unwrappedResult.UnwrapCaretPoint();
856 // MOZ_KnownLive(*styledElement): aElement's lifetime must be guarantted
857 // by the caller because of MOZ_CAN_RUN_SCRIPT method.
858 Result<EditorDOMPoint, nsresult> unwrapStyledElementResult =
859 RemoveContainerWithTransaction(MOZ_KnownLive(*styledElement));
860 if (MOZ_UNLIKELY(unwrapStyledElementResult.isErr())) {
861 NS_WARNING("HTMLEditor::RemoveContainerWithTransaction() failed");
862 return unwrapStyledElementResult.unwrapErr();
864 if (unwrapStyledElementResult.inspect().IsSet()) {
865 pointToPutCaret = unwrapStyledElementResult.unwrap();
868 if (!AllowsTransactionsToChangeSelection() || !pointToPutCaret.IsSet()) {
869 return NS_OK;
871 rv = CollapseSelectionTo(pointToPutCaret);
872 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
873 "EditorBase::CollapseSelectionTo() failed");
874 return rv;
877 NS_IMETHODIMP HTMLEditor::SetSnapToGridEnabled(bool aEnabled) {
878 mSnapToGridEnabled = aEnabled;
879 return NS_OK;
882 NS_IMETHODIMP HTMLEditor::GetSnapToGridEnabled(bool* aIsEnabled) {
883 *aIsEnabled = mSnapToGridEnabled;
884 return NS_OK;
887 NS_IMETHODIMP HTMLEditor::SetGridSize(uint32_t aSize) {
888 mGridSize = aSize;
889 return NS_OK;
892 NS_IMETHODIMP HTMLEditor::GetGridSize(uint32_t* aSize) {
893 *aSize = mGridSize;
894 return NS_OK;
897 nsresult HTMLEditor::SetTopAndLeftWithTransaction(
898 nsStyledElement& aStyledElement, int32_t aX, int32_t aY) {
899 AutoPlaceholderBatch treatAsOneTransaction(
900 *this, ScrollSelectionIntoView::Yes, __FUNCTION__);
901 nsresult rv;
902 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
903 *nsGkAtoms::left, aX);
904 if (rv == NS_ERROR_EDITOR_DESTROYED) {
905 NS_WARNING(
906 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
907 "destroyed the editor");
908 return NS_ERROR_EDITOR_DESTROYED;
910 NS_WARNING_ASSERTION(
911 NS_SUCCEEDED(rv),
912 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) "
913 "failed, but ignored");
914 rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction(*this, aStyledElement,
915 *nsGkAtoms::top, aY);
916 if (rv == NS_ERROR_EDITOR_DESTROYED) {
917 NS_WARNING(
918 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
919 "destroyed the editor");
920 return NS_ERROR_EDITOR_DESTROYED;
922 NS_WARNING_ASSERTION(
923 NS_SUCCEEDED(rv),
924 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) "
925 "failed, but ignored");
926 return NS_OK;
929 nsresult HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(
930 Element& aElement, nsAString& aReturn) {
931 // we are going to outline the positioned element and bring it to the
932 // front to overlap any other element intersecting with it. But
933 // first, let's see what's the background and foreground colors of the
934 // positioned element.
935 // if background-image computed value is 'none,
936 // If the background color is 'auto' and R G B values of the foreground are
937 // each above #d0, use a black background
938 // If the background color is 'auto' and at least one of R G B values of
939 // the foreground is below #d0, use a white background
940 // Otherwise don't change background/foreground
941 aReturn.Truncate();
943 nsAutoString backgroundImageValue;
944 nsresult rv = CSSEditUtils::GetComputedProperty(
945 aElement, *nsGkAtoms::background_image, backgroundImageValue);
946 if (NS_FAILED(rv)) {
947 NS_WARNING(
948 "CSSEditUtils::GetComputedProperty(nsGkAtoms::background_image) "
949 "failed");
950 return rv;
952 if (!backgroundImageValue.EqualsLiteral("none")) {
953 return NS_OK;
956 nsAutoString backgroundColorValue;
957 rv = CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::backgroundColor,
958 backgroundColorValue);
959 if (NS_FAILED(rv)) {
960 NS_WARNING(
961 "CSSEditUtils::GetComputedProperty(nsGkAtoms::backgroundColor) "
962 "failed");
963 return rv;
965 if (!backgroundColorValue.EqualsLiteral("rgba(0, 0, 0, 0)")) {
966 return NS_OK;
969 RefPtr<const ComputedStyle> style =
970 nsComputedDOMStyle::GetComputedStyle(&aElement);
971 if (NS_WARN_IF(Destroyed())) {
972 return NS_ERROR_EDITOR_DESTROYED;
974 if (!style) {
975 NS_WARNING("nsComputedDOMStyle::GetComputedStyle() failed");
976 return NS_ERROR_FAILURE;
979 static const uint8_t kBlackBgTrigger = 0xd0;
981 auto color = style->StyleText()->mColor.ToColor();
982 if (NS_GET_R(color) >= kBlackBgTrigger &&
983 NS_GET_G(color) >= kBlackBgTrigger &&
984 NS_GET_B(color) >= kBlackBgTrigger) {
985 aReturn.AssignLiteral("black");
986 } else {
987 aReturn.AssignLiteral("white");
990 return NS_OK;
993 } // namespace mozilla