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