HTMLEditorObjectResizer.cpp (53900B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "ErrorList.h" 7 #include "HTMLEditor.h" 8 9 #include "CSSEditUtils.h" 10 #include "HTMLEditorEventListener.h" 11 #include "HTMLEditUtils.h" 12 13 #include "mozilla/DebugOnly.h" 14 #include "mozilla/LookAndFeel.h" 15 #include "mozilla/MathAlgorithms.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/Event.h" 21 #include "mozilla/dom/MouseEvent.h" 22 #include "mozilla/dom/EventTarget.h" 23 #include "nsAtom.h" 24 #include "nsAString.h" 25 #include "nsCOMPtr.h" 26 #include "nsDebug.h" 27 #include "nsDOMTokenList.h" 28 #include "nsError.h" 29 #include "nsGkAtoms.h" 30 #include "nsIContent.h" 31 #include "nsID.h" 32 #include "mozilla/dom/Document.h" 33 #include "nsISupportsUtils.h" 34 #include "nsPIDOMWindow.h" 35 #include "nsReadableUtils.h" 36 #include "nsString.h" 37 #include "nsStringFwd.h" 38 #include "nsStyledElement.h" 39 #include "nsTextNode.h" 40 #include "nscore.h" 41 #include <algorithm> 42 43 #define kTopLeft u"nw"_ns 44 #define kTop u"n"_ns 45 #define kTopRight u"ne"_ns 46 #define kLeft u"w"_ns 47 #define kRight u"e"_ns 48 #define kBottomLeft u"sw"_ns 49 #define kBottom u"s"_ns 50 #define kBottomRight u"se"_ns 51 52 namespace mozilla { 53 54 using namespace dom; 55 56 /****************************************************************************** 57 * mozilla::HTMLEditor 58 ******************************************************************************/ 59 60 ManualNACPtr HTMLEditor::CreateResizer(int16_t aLocation, 61 nsIContent& aParentContent) { 62 ManualNACPtr resizer = CreateAnonymousElement(nsGkAtoms::span, aParentContent, 63 u"mozResizer"_ns, false); 64 if (!resizer) { 65 NS_WARNING( 66 "HTMLEditor::CreateAnonymousElement(nsGkAtoms::span, mozResizer) " 67 "failed"); 68 return nullptr; 69 } 70 71 // add the mouse listener so we can detect a click on a resizer 72 DebugOnly<nsresult> rvIgnored = 73 resizer->AddEventListener(u"mousedown"_ns, mEventListener, true); 74 NS_WARNING_ASSERTION( 75 NS_SUCCEEDED(rvIgnored), 76 "EventTarget::AddEventListener(mousedown) failed, but ignored"); 77 nsAutoString locationStr; 78 switch (aLocation) { 79 case nsIHTMLObjectResizer::eTopLeft: 80 locationStr = kTopLeft; 81 break; 82 case nsIHTMLObjectResizer::eTop: 83 locationStr = kTop; 84 break; 85 case nsIHTMLObjectResizer::eTopRight: 86 locationStr = kTopRight; 87 break; 88 89 case nsIHTMLObjectResizer::eLeft: 90 locationStr = kLeft; 91 break; 92 case nsIHTMLObjectResizer::eRight: 93 locationStr = kRight; 94 break; 95 96 case nsIHTMLObjectResizer::eBottomLeft: 97 locationStr = kBottomLeft; 98 break; 99 case nsIHTMLObjectResizer::eBottom: 100 locationStr = kBottom; 101 break; 102 case nsIHTMLObjectResizer::eBottomRight: 103 locationStr = kBottomRight; 104 break; 105 } 106 107 if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, 108 locationStr, true))) { 109 NS_WARNING("Element::SetAttr(nsGkAtoms::anonlocation) failed"); 110 return nullptr; 111 } 112 nsAutoString cursor = u"cursor: "_ns + locationStr + u"-resize"_ns; 113 if (NS_FAILED(resizer->SetAttr(kNameSpaceID_None, nsGkAtoms::style, cursor, 114 true))) { 115 NS_WARNING("Element::SetAttr(nsGkAtoms::style) failed"); 116 return nullptr; 117 } 118 return resizer; 119 } 120 121 ManualNACPtr HTMLEditor::CreateShadow(nsIContent& aParentContent, 122 Element& aOriginalObject) { 123 // let's create an image through the element factory 124 RefPtr<nsAtom> name; 125 if (HTMLEditUtils::IsImageElement(aOriginalObject)) { 126 name = nsGkAtoms::img; 127 } else { 128 name = nsGkAtoms::span; 129 } 130 131 return CreateAnonymousElement(name, aParentContent, u"mozResizingShadow"_ns, 132 true); 133 } 134 135 ManualNACPtr HTMLEditor::CreateResizingInfo(nsIContent& aParentContent) { 136 // let's create an info box through the element factory 137 return CreateAnonymousElement(nsGkAtoms::span, aParentContent, 138 u"mozResizingInfo"_ns, true); 139 } 140 141 nsresult HTMLEditor::SetAllResizersPosition() { 142 if (NS_WARN_IF(!mTopLeftHandle)) { 143 return NS_ERROR_FAILURE; // There are no resizers. 144 } 145 146 int32_t x = mResizedObjectX; 147 int32_t y = mResizedObjectY; 148 int32_t w = mResizedObjectWidth; 149 int32_t h = mResizedObjectHeight; 150 151 nsAutoString value; 152 float resizerWidth, resizerHeight; 153 RefPtr<nsAtom> dummyUnit; 154 DebugOnly<nsresult> rvIgnored = NS_OK; 155 OwningNonNull<Element> topLeftHandle = *mTopLeftHandle.get(); 156 // XXX Do we really need to computed value rather than specified value? 157 // Because it's an anonymous node. 158 rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle, 159 *nsGkAtoms::width, value); 160 if (NS_WARN_IF(Destroyed())) { 161 return NS_ERROR_EDITOR_DESTROYED; 162 } 163 if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) { 164 return NS_ERROR_FAILURE; 165 } 166 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 167 "CSSEditUtils::GetComputedProperty(nsGkAtoms::width) " 168 "failed, but ignored"); 169 rvIgnored = CSSEditUtils::GetComputedProperty(*topLeftHandle, 170 *nsGkAtoms::height, value); 171 if (NS_WARN_IF(Destroyed())) { 172 return NS_ERROR_EDITOR_DESTROYED; 173 } 174 if (NS_WARN_IF(topLeftHandle != mTopLeftHandle)) { 175 return NS_ERROR_FAILURE; 176 } 177 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 178 "CSSEditUtils::GetComputedProperty(nsGkAtoms::height) " 179 "failed, but ignored"); 180 CSSEditUtils::ParseLength(value, &resizerWidth, getter_AddRefs(dummyUnit)); 181 CSSEditUtils::ParseLength(value, &resizerHeight, getter_AddRefs(dummyUnit)); 182 183 int32_t rw = static_cast<int32_t>((resizerWidth + 1) / 2); 184 int32_t rh = static_cast<int32_t>((resizerHeight + 1) / 2); 185 186 // While moving each resizer, mutation event listener may hide the resizers. 187 // And in worst case, new resizers may be recreated. So, we need to store 188 // all resizers here, and then, if we detect a resizer is removed or replaced, 189 // we should do nothing anymore. 190 // FYI: Note that only checking if mTopLeftHandle is replaced is enough. 191 // We're may be in hot path if user resizes an element a lot. So, 192 // we should just add-ref mTopLeftHandle. 193 auto setHandlePosition = 194 [this](ManualNACPtr& aHandleElement, int32_t aNewX, int32_t aNewY) 195 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION -> nsresult { 196 RefPtr<nsStyledElement> handleStyledElement = 197 nsStyledElement::FromNodeOrNull(aHandleElement.get()); 198 if (!handleStyledElement) { 199 return NS_OK; 200 } 201 nsresult rv = SetAnonymousElementPositionWithoutTransaction( 202 *handleStyledElement, aNewX, aNewY); 203 if (NS_FAILED(rv)) { 204 NS_WARNING( 205 "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() " 206 "failed"); 207 return rv; 208 } 209 return NS_WARN_IF(handleStyledElement != aHandleElement.get()) 210 ? NS_ERROR_FAILURE 211 : NS_OK; 212 }; 213 nsresult rv; 214 rv = setHandlePosition(mTopLeftHandle, x - rw, y - rh); 215 if (NS_FAILED(rv)) { 216 NS_WARNING("Failed to set top-left handle position"); 217 return rv; 218 } 219 rv = setHandlePosition(mTopHandle, x + w / 2 - rw, y - rh); 220 if (NS_FAILED(rv)) { 221 NS_WARNING("Failed to set top handle position"); 222 return rv; 223 } 224 rv = setHandlePosition(mTopRightHandle, x + w - rw - 1, y - rh); 225 if (NS_FAILED(rv)) { 226 NS_WARNING("Failed to set top-right handle position"); 227 return rv; 228 } 229 230 rv = setHandlePosition(mLeftHandle, x - rw, y + h / 2 - rh); 231 if (NS_FAILED(rv)) { 232 NS_WARNING("Failed to set left handle position"); 233 return rv; 234 } 235 rv = setHandlePosition(mRightHandle, x + w - rw - 1, y + h / 2 - rh); 236 if (NS_FAILED(rv)) { 237 NS_WARNING("Failed to set right handle position"); 238 return rv; 239 } 240 241 rv = setHandlePosition(mBottomLeftHandle, x - rw, y + h - rh - 1); 242 if (NS_FAILED(rv)) { 243 NS_WARNING("Failed to set bottom-left handle position"); 244 return rv; 245 } 246 rv = setHandlePosition(mBottomHandle, x + w / 2 - rw, y + h - rh - 1); 247 if (NS_FAILED(rv)) { 248 NS_WARNING("Failed to set bottom handle position"); 249 return rv; 250 } 251 rv = setHandlePosition(mBottomRightHandle, x + w - rw - 1, y + h - rh - 1); 252 if (NS_FAILED(rv)) { 253 NS_WARNING("Failed to set bottom-right handle position"); 254 return rv; 255 } 256 257 return NS_OK; 258 } 259 260 nsresult HTMLEditor::RefreshResizers() { 261 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); 262 if (NS_WARN_IF(!editActionData.CanHandle())) { 263 return NS_ERROR_NOT_INITIALIZED; 264 } 265 266 nsresult rv = RefreshResizersInternal(); 267 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 268 "HTMLEditor::RefreshResizersInternal() failed"); 269 return EditorBase::ToGenericNSResult(rv); 270 } 271 272 nsresult HTMLEditor::RefreshResizersInternal() { 273 MOZ_ASSERT(IsEditActionDataAvailable()); 274 275 // Don't warn even if resizers are not visible since script cannot check 276 // if they are visible and this is non-virtual method. So, the cost of 277 // calling this can be ignored. 278 if (!mResizedObject) { 279 return NS_OK; 280 } 281 282 OwningNonNull<Element> resizedObject = *mResizedObject; 283 nsresult rv = GetPositionAndDimensions( 284 resizedObject, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, 285 mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, 286 mResizedObjectMarginLeft, mResizedObjectMarginTop); 287 if (NS_FAILED(rv)) { 288 NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed"); 289 return rv; 290 } 291 if (NS_WARN_IF(resizedObject != mResizedObject)) { 292 return NS_ERROR_FAILURE; 293 } 294 295 rv = SetAllResizersPosition(); 296 if (NS_FAILED(rv)) { 297 NS_WARNING("HTMLEditor::SetAllResizersPosition() failed"); 298 return rv; 299 } 300 if (NS_WARN_IF(resizedObject != mResizedObject)) { 301 return NS_ERROR_FAILURE; 302 } 303 304 MOZ_ASSERT( 305 mResizingShadow, 306 "SetAllResizersPosition() should return error if resizers are hidden"); 307 RefPtr<Element> resizingShadow = mResizingShadow.get(); 308 rv = SetShadowPosition(*resizingShadow, resizedObject, mResizedObjectX, 309 mResizedObjectY); 310 if (NS_FAILED(rv)) { 311 NS_WARNING("HTMLEditor::SetShadowPosition() failed"); 312 return rv; 313 } 314 if (NS_WARN_IF(resizedObject != mResizedObject)) { 315 return NS_ERROR_FAILURE; 316 } 317 return NS_OK; 318 } 319 320 nsresult HTMLEditor::ShowResizersInternal(Element& aResizedElement) { 321 // When we have visible resizers, we cannot show new resizers. 322 // So, the caller should call HideResizersInternal() first if this 323 // returns error. 324 if (NS_WARN_IF(mResizedObject)) { 325 return NS_ERROR_UNEXPECTED; 326 } 327 328 nsCOMPtr<nsIContent> parentContent = aResizedElement.GetParent(); 329 if (NS_WARN_IF(!parentContent)) { 330 return NS_ERROR_FAILURE; 331 } 332 333 const RefPtr<Element> editingHost = ComputeEditingHost(); 334 if (NS_WARN_IF(!editingHost) || 335 NS_WARN_IF(!aResizedElement.IsInclusiveDescendantOf(editingHost))) { 336 return NS_ERROR_UNEXPECTED; 337 } 338 339 // Let's create and setup resizers. If we failed something, we should 340 // cancel everything which we do in this method. 341 do { 342 mResizedObject = &aResizedElement; 343 344 // The resizers and the shadow will be anonymous siblings of the element. 345 // Note that creating a resizer or shadow may causes calling 346 // HideRisizersInternal() via a mutation event listener. So, we should 347 // store new resizer to a local variable, then, check: 348 // - whether creating resizer is already set to the member or not 349 // - whether resizing element is changed to another element 350 // If showing resizers are canceled, we hit the latter check. 351 // If resizers for another element is shown during this, we hit the latter 352 // check too. 353 // If resizers are just shown again for same element, we hit the former 354 // check. 355 ManualNACPtr newResizer = 356 CreateResizer(nsIHTMLObjectResizer::eTopLeft, *parentContent); 357 if (!newResizer) { 358 NS_WARNING("HTMLEditor::CreateResizer(eTopLeft) failed"); 359 break; 360 } 361 if (NS_WARN_IF(mTopLeftHandle) || 362 NS_WARN_IF(mResizedObject != &aResizedElement)) { 363 // Don't hide current resizers in this case because they are not what 364 // we're creating. 365 return NS_ERROR_FAILURE; 366 } 367 mTopLeftHandle = std::move(newResizer); 368 newResizer = CreateResizer(nsIHTMLObjectResizer::eTop, *parentContent); 369 if (!newResizer) { 370 NS_WARNING("HTMLEditor::CreateResizer(eTop) failed"); 371 break; 372 } 373 if (NS_WARN_IF(mTopHandle) || 374 NS_WARN_IF(mResizedObject != &aResizedElement)) { 375 return NS_ERROR_FAILURE; 376 } 377 mTopHandle = std::move(newResizer); 378 newResizer = CreateResizer(nsIHTMLObjectResizer::eTopRight, *parentContent); 379 if (!newResizer) { 380 NS_WARNING("HTMLEditor::CreateResizer(eTopRight) failed"); 381 break; 382 } 383 if (NS_WARN_IF(mTopRightHandle) || 384 NS_WARN_IF(mResizedObject != &aResizedElement)) { 385 return NS_ERROR_FAILURE; 386 } 387 mTopRightHandle = std::move(newResizer); 388 389 newResizer = CreateResizer(nsIHTMLObjectResizer::eLeft, *parentContent); 390 if (!newResizer) { 391 NS_WARNING("HTMLEditor::CreateResizer(eLeft) failed"); 392 break; 393 } 394 if (NS_WARN_IF(mLeftHandle) || 395 NS_WARN_IF(mResizedObject != &aResizedElement)) { 396 return NS_ERROR_FAILURE; 397 } 398 mLeftHandle = std::move(newResizer); 399 newResizer = CreateResizer(nsIHTMLObjectResizer::eRight, *parentContent); 400 if (!newResizer) { 401 NS_WARNING("HTMLEditor::CreateResizer(eRight) failed"); 402 break; 403 } 404 if (NS_WARN_IF(mRightHandle) || 405 NS_WARN_IF(mResizedObject != &aResizedElement)) { 406 return NS_ERROR_FAILURE; 407 } 408 mRightHandle = std::move(newResizer); 409 410 newResizer = 411 CreateResizer(nsIHTMLObjectResizer::eBottomLeft, *parentContent); 412 if (!newResizer) { 413 NS_WARNING("HTMLEditor::CreateResizer(eBottomLeft) failed"); 414 break; 415 } 416 if (NS_WARN_IF(mBottomLeftHandle) || 417 NS_WARN_IF(mResizedObject != &aResizedElement)) { 418 return NS_ERROR_FAILURE; 419 } 420 mBottomLeftHandle = std::move(newResizer); 421 newResizer = CreateResizer(nsIHTMLObjectResizer::eBottom, *parentContent); 422 if (!newResizer) { 423 NS_WARNING("HTMLEditor::CreateResizer(eBottom) failed"); 424 break; 425 } 426 if (NS_WARN_IF(mBottomHandle) || 427 NS_WARN_IF(mResizedObject != &aResizedElement)) { 428 return NS_ERROR_FAILURE; 429 } 430 mBottomHandle = std::move(newResizer); 431 newResizer = 432 CreateResizer(nsIHTMLObjectResizer::eBottomRight, *parentContent); 433 if (!newResizer) { 434 NS_WARNING("HTMLEditor::CreateResizer(eBottomRight) failed"); 435 break; 436 } 437 if (NS_WARN_IF(mBottomRightHandle) || 438 NS_WARN_IF(mResizedObject != &aResizedElement)) { 439 return NS_ERROR_FAILURE; 440 } 441 mBottomRightHandle = std::move(newResizer); 442 443 // Store the last resizer which we created. This is useful when we 444 // need to check whether our resizers are hiddedn and recreated another 445 // set of resizers or not. 446 RefPtr<Element> createdBottomRightHandle = mBottomRightHandle.get(); 447 448 nsresult rv = GetPositionAndDimensions( 449 aResizedElement, mResizedObjectX, mResizedObjectY, mResizedObjectWidth, 450 mResizedObjectHeight, mResizedObjectBorderLeft, mResizedObjectBorderTop, 451 mResizedObjectMarginLeft, mResizedObjectMarginTop); 452 if (NS_FAILED(rv)) { 453 NS_WARNING("HTMLEditor::GetPositionAndDimensions() failed"); 454 break; 455 } 456 457 // and let's set their absolute positions in the document 458 rv = SetAllResizersPosition(); 459 if (NS_FAILED(rv)) { 460 NS_WARNING("HTMLEditor::SetAllResizersPosition() failed"); 461 if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) { 462 return NS_ERROR_FAILURE; 463 } 464 break; 465 } 466 467 // now, let's create the resizing shadow 468 ManualNACPtr newShadow = CreateShadow(*parentContent, aResizedElement); 469 if (!newShadow) { 470 NS_WARNING("HTMLEditor::CreateShadow() failed"); 471 break; 472 } 473 if (NS_WARN_IF(mResizingShadow) || 474 NS_WARN_IF(mResizedObject != &aResizedElement)) { 475 return NS_ERROR_FAILURE; 476 } 477 mResizingShadow = std::move(newShadow); 478 479 // and set its position 480 RefPtr<Element> resizingShadow = mResizingShadow.get(); 481 rv = SetShadowPosition(*resizingShadow, aResizedElement, mResizedObjectX, 482 mResizedObjectY); 483 if (NS_FAILED(rv)) { 484 NS_WARNING("HTMLEditor::SetShadowPosition() failed"); 485 if (NS_WARN_IF(mBottomRightHandle.get() != createdBottomRightHandle)) { 486 return NS_ERROR_FAILURE; 487 } 488 break; 489 } 490 491 // and then the resizing info tooltip 492 ManualNACPtr newResizingInfo = CreateResizingInfo(*parentContent); 493 if (!newResizingInfo) { 494 NS_WARNING("HTMLEditor::CreateResizingInfo() failed"); 495 break; 496 } 497 if (NS_WARN_IF(mResizingInfo) || 498 NS_WARN_IF(mResizedObject != &aResizedElement)) { 499 return NS_ERROR_FAILURE; 500 } 501 mResizingInfo = std::move(newResizingInfo); 502 503 // and listen to the "resize" event on the window first, get the 504 // window from the document... 505 if (NS_WARN_IF(!mEventListener)) { 506 break; 507 } 508 509 rv = static_cast<HTMLEditorEventListener*>(mEventListener.get()) 510 ->ListenToWindowResizeEvent(true); 511 if (NS_FAILED(rv)) { 512 NS_WARNING( 513 "HTMLEditorEventListener::ListenToWindowResizeEvent(true) failed"); 514 break; 515 } 516 517 MOZ_ASSERT(mResizedObject == &aResizedElement); 518 return NS_OK; 519 } while (true); 520 521 DebugOnly<nsresult> rv = HideResizersInternal(); 522 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 523 "HTMLEditor::HideResizersInternal() failed to clean up"); 524 return NS_ERROR_FAILURE; 525 } 526 527 NS_IMETHODIMP HTMLEditor::HideResizers() { 528 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); 529 if (NS_WARN_IF(!editActionData.CanHandle())) { 530 return NS_ERROR_NOT_INITIALIZED; 531 } 532 533 nsresult rv = HideResizersInternal(); 534 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 535 "HTMLEditor::HideResizersInternal() failed"); 536 return EditorBase::ToGenericNSResult(rv); 537 } 538 539 nsresult HTMLEditor::HideResizersInternal() { 540 // Don't warn even if resizers are visible since script cannot check 541 // if they are visible and this is non-virtual method. So, the cost of 542 // calling this can be ignored. 543 if (!mResizedObject) { 544 return NS_OK; 545 } 546 547 // get the presshell's document observer interface. 548 RefPtr<PresShell> presShell = GetPresShell(); 549 NS_WARNING_ASSERTION(presShell, "There is no presShell"); 550 // We allow the pres shell to be null; when it is, we presume there 551 // are no document observers to notify, but we still want to 552 // UnbindFromTree. 553 554 constexpr auto mousedown = u"mousedown"_ns; 555 556 // HTMLEditor should forget all members related to resizers first since 557 // removing a part of UI may cause showing the resizers again. In such 558 // case, the members may be overwritten by ShowResizers() and this will 559 // lose the chance to release the old resizers. 560 ManualNACPtr topLeftHandle(std::move(mTopLeftHandle)); 561 ManualNACPtr topHandle(std::move(mTopHandle)); 562 ManualNACPtr topRightHandle(std::move(mTopRightHandle)); 563 ManualNACPtr leftHandle(std::move(mLeftHandle)); 564 ManualNACPtr rightHandle(std::move(mRightHandle)); 565 ManualNACPtr bottomLeftHandle(std::move(mBottomLeftHandle)); 566 ManualNACPtr bottomHandle(std::move(mBottomHandle)); 567 ManualNACPtr bottomRightHandle(std::move(mBottomRightHandle)); 568 ManualNACPtr resizingShadow(std::move(mResizingShadow)); 569 ManualNACPtr resizingInfo(std::move(mResizingInfo)); 570 RefPtr<Element> activatedHandle(std::move(mActivatedHandle)); 571 RefPtr<Element> resizedObject(std::move(mResizedObject)); 572 573 // Remvoe all handles. 574 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 575 std::move(topLeftHandle), presShell); 576 577 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 578 std::move(topHandle), presShell); 579 580 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 581 std::move(topRightHandle), presShell); 582 583 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 584 std::move(leftHandle), presShell); 585 586 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 587 std::move(rightHandle), presShell); 588 589 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 590 std::move(bottomLeftHandle), presShell); 591 592 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 593 std::move(bottomHandle), presShell); 594 595 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 596 std::move(bottomRightHandle), presShell); 597 598 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 599 std::move(resizingShadow), presShell); 600 601 RemoveListenerAndDeleteRef(mousedown, mEventListener, true, 602 std::move(resizingInfo), presShell); 603 604 // Remove active state of a resizer. 605 if (activatedHandle) { 606 activatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors()); 607 } 608 609 if (!mEventListener) { 610 return NS_OK; 611 } 612 613 nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get()) 614 ->ListenToMouseMoveEventForResizers(false); 615 if (NS_FAILED(rv)) { 616 NS_WARNING( 617 "HTMLEditorEventListener::ListenToMouseMoveEventForResizers(false) " 618 "failed"); 619 return rv; 620 } 621 622 // Remove resize event listener from the window. 623 if (!mEventListener) { 624 return NS_OK; 625 } 626 627 rv = static_cast<HTMLEditorEventListener*>(mEventListener.get()) 628 ->ListenToWindowResizeEvent(false); 629 NS_WARNING_ASSERTION( 630 NS_SUCCEEDED(rv), 631 "HTMLEditorEventListener::ListenToWindowResizeEvent(false) failed"); 632 return rv; 633 } 634 635 void HTMLEditor::HideShadowAndInfo() { 636 if (mResizingShadow) { 637 DebugOnly<nsresult> rvIgnored = mResizingShadow->SetAttr( 638 kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true); 639 NS_WARNING_ASSERTION( 640 NS_SUCCEEDED(rvIgnored), 641 "Element::SetAttr(nsGkAtoms::hidden) failed, but ignored"); 642 } 643 if (mResizingInfo) { 644 DebugOnly<nsresult> rvIgnored = mResizingInfo->SetAttr( 645 kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true); 646 NS_WARNING_ASSERTION( 647 NS_SUCCEEDED(rvIgnored), 648 "Element::SetAttr(nsGkAtoms::hidden, hidden) failed, but ignored"); 649 } 650 } 651 652 nsresult HTMLEditor::StartResizing(Element& aHandleElement) { 653 mIsResizing = true; 654 mActivatedHandle = &aHandleElement; 655 mActivatedHandle->ClassList()->Add(u"active"_ns, IgnoreErrors()); 656 if (NS_WARN_IF(Destroyed())) { 657 return NS_ERROR_EDITOR_DESTROYED; 658 } 659 660 // do we want to preserve ratio or not? 661 const bool preserveRatio = 662 mResizedObject && HTMLEditUtils::IsImageElement(*mResizedObject); 663 664 // the way we change the position/size of the shadow depends on 665 // the handle 666 nsAutoString locationStr; 667 mActivatedHandle->GetAttr(nsGkAtoms::anonlocation, locationStr); 668 if (locationStr.Equals(kTopLeft)) { 669 SetResizeIncrements(1, 1, -1, -1, preserveRatio); 670 } else if (locationStr.Equals(kTop)) { 671 SetResizeIncrements(0, 1, 0, -1, false); 672 } else if (locationStr.Equals(kTopRight)) { 673 SetResizeIncrements(0, 1, 1, -1, preserveRatio); 674 } else if (locationStr.Equals(kLeft)) { 675 SetResizeIncrements(1, 0, -1, 0, false); 676 } else if (locationStr.Equals(kRight)) { 677 SetResizeIncrements(0, 0, 1, 0, false); 678 } else if (locationStr.Equals(kBottomLeft)) { 679 SetResizeIncrements(1, 0, -1, 1, preserveRatio); 680 } else if (locationStr.Equals(kBottom)) { 681 SetResizeIncrements(0, 0, 0, 1, false); 682 } else if (locationStr.Equals(kBottomRight)) { 683 SetResizeIncrements(0, 0, 1, 1, preserveRatio); 684 } 685 686 // make the shadow appear 687 DebugOnly<nsresult> rvIgnored = 688 mResizingShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true); 689 if (NS_WARN_IF(Destroyed())) { 690 return NS_ERROR_EDITOR_DESTROYED; 691 } 692 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 693 "Element::UnsetAttr(nsGkAtoms::hidden) failed"); 694 695 // position it 696 if (RefPtr<nsStyledElement> resizingShadowStyledElement = 697 nsStyledElement::FromNodeOrNull(mResizingShadow.get())) { 698 nsresult rv; 699 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 700 *this, *resizingShadowStyledElement, *nsGkAtoms::width, 701 mResizedObjectWidth); 702 if (rv == NS_ERROR_EDITOR_DESTROYED) { 703 NS_WARNING( 704 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(" 705 "nsGkAtoms::width) destroyed the editor"); 706 return NS_ERROR_EDITOR_DESTROYED; 707 } 708 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 709 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(" 710 "nsGkAtoms::width) failed"); 711 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 712 *this, *resizingShadowStyledElement, *nsGkAtoms::height, 713 mResizedObjectHeight); 714 if (rv == NS_ERROR_EDITOR_DESTROYED) { 715 NS_WARNING( 716 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(" 717 "nsGkAtoms::height) destroyed the editor"); 718 return NS_ERROR_EDITOR_DESTROYED; 719 } 720 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 721 "CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction(" 722 "nsGkAtoms::height) failed"); 723 } 724 725 // add a mouse move listener to the editor 726 if (NS_WARN_IF(!mEventListener)) { 727 return NS_ERROR_NOT_INITIALIZED; 728 } 729 nsresult rv = static_cast<HTMLEditorEventListener*>(mEventListener.get()) 730 ->ListenToMouseMoveEventForResizers(true); 731 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 732 "HTMLEditorEventListener::" 733 "ListenToMouseMoveEventForResizers(true) failed"); 734 return rv; 735 } 736 737 nsresult HTMLEditor::StartToDragResizerOrHandleDragGestureOnGrabber( 738 MouseEvent& aMouseDownEvent, Element& aEventTargetElement) { 739 MOZ_ASSERT(aMouseDownEvent.GetExplicitOriginalTarget() == 740 &aEventTargetElement); 741 MOZ_ASSERT(!aMouseDownEvent.DefaultPrevented()); 742 MOZ_ASSERT(aMouseDownEvent.WidgetEventPtr()->mMessage == eMouseDown); 743 744 nsDOMTokenList& classList = *aEventTargetElement.ClassList(); 745 746 if (classList.Contains(u"mozResizer"_ns)) { 747 AutoEditActionDataSetter editActionData(*this, 748 EditAction::eResizingElement); 749 if (NS_WARN_IF(!editActionData.CanHandle())) { 750 return NS_ERROR_NOT_INITIALIZED; 751 } 752 753 // If we have an anonymous element and that element is a resizer, 754 // let's start resizing! 755 aMouseDownEvent.PreventDefault(); 756 mOriginalX = aMouseDownEvent.ClientX(); 757 mOriginalY = aMouseDownEvent.ClientY(); 758 nsresult rv = StartResizing(aEventTargetElement); 759 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 760 "HTMLEditor::StartResizing() failed"); 761 return EditorBase::ToGenericNSResult(rv); 762 } 763 764 if (classList.Contains(u"mozGrabber"_ns)) { 765 AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement); 766 if (NS_WARN_IF(!editActionData.CanHandle())) { 767 return NS_ERROR_NOT_INITIALIZED; 768 } 769 770 // If we have an anonymous element and that element is a grabber, 771 // let's start moving the element! 772 mOriginalX = aMouseDownEvent.ClientX(); 773 mOriginalY = aMouseDownEvent.ClientY(); 774 nsresult rv = GrabberClicked(); 775 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 776 "HTMLEditor::GrabberClicked() failed"); 777 return EditorBase::ToGenericNSResult(rv); 778 } 779 780 return NS_OK; 781 } 782 783 nsresult HTMLEditor::StopDraggingResizerOrGrabberAt( 784 const CSSIntPoint& aClientPoint) { 785 if (mIsResizing) { 786 AutoEditActionDataSetter editActionData(*this, EditAction::eResizeElement); 787 if (NS_WARN_IF(!editActionData.CanHandle())) { 788 return NS_ERROR_NOT_INITIALIZED; 789 } 790 791 // we are resizing and release the mouse button, so let's 792 // end the resizing process 793 mIsResizing = false; 794 HideShadowAndInfo(); 795 796 nsresult rv = editActionData.MaybeDispatchBeforeInputEvent(); 797 if (NS_FAILED(rv)) { 798 NS_WARNING_ASSERTION( 799 rv == NS_ERROR_EDITOR_ACTION_CANCELED, 800 "EditorBase::MaybeDispatchBeforeInputEvent(), failed"); 801 return EditorBase::ToGenericNSResult(rv); 802 } 803 804 rv = SetFinalSizeWithTransaction(aClientPoint.x, aClientPoint.y); 805 if (rv == NS_ERROR_EDITOR_DESTROYED) { 806 NS_WARNING( 807 "HTMLEditor::SetFinalSizeWithTransaction() destroyed the editor"); 808 return NS_ERROR_EDITOR_DESTROYED; 809 } 810 NS_WARNING_ASSERTION( 811 NS_SUCCEEDED(rv), 812 "HTMLEditor::SetFinalSizeWithTransaction() failed, but ignored"); 813 return NS_OK; 814 } 815 816 if (mIsMoving || mGrabberClicked) { 817 AutoEditActionDataSetter editActionData(*this, EditAction::eMoveElement); 818 nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent(); 819 if (rv != NS_ERROR_EDITOR_ACTION_CANCELED && NS_WARN_IF(NS_FAILED(rv))) { 820 NS_WARNING("CanHandleAndMaybeDispatchBeforeInputEvent() failed"); 821 return EditorBase::ToGenericNSResult(rv); 822 } 823 824 if (mIsMoving) { 825 DebugOnly<nsresult> rvIgnored = mPositioningShadow->SetAttr( 826 kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true); 827 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 828 "Element::SetAttr(nsGkAtoms::hidden) failed"); 829 if (rv != NS_ERROR_EDITOR_ACTION_CANCELED) { 830 SetFinalPosition(aClientPoint.x, aClientPoint.y); 831 } 832 } 833 if (mGrabberClicked) { 834 DebugOnly<nsresult> rvIgnored = EndMoving(); 835 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 836 "HTMLEditor::EndMoving() failed"); 837 } 838 return EditorBase::ToGenericNSResult(rv); 839 } 840 841 return NS_OK; 842 } 843 844 void HTMLEditor::SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, 845 int32_t aH, bool aPreserveRatio) { 846 mXIncrementFactor = aX; 847 mYIncrementFactor = aY; 848 mWidthIncrementFactor = aW; 849 mHeightIncrementFactor = aH; 850 mPreserveRatio = aPreserveRatio; 851 } 852 853 nsresult HTMLEditor::SetResizingInfoPosition(int32_t aX, int32_t aY, int32_t aW, 854 int32_t aH) { 855 // Determine the position of the resizing info box based upon the new 856 // position and size of the element (aX, aY, aW, aH), and which 857 // resizer is the "activated handle". For example, place the resizing 858 // info box at the bottom-right corner of the new element, if the element 859 // is being resized by the bottom-right resizer. 860 int32_t infoXPosition; 861 int32_t infoYPosition; 862 863 if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mLeftHandle || 864 mActivatedHandle == mBottomLeftHandle) { 865 infoXPosition = aX; 866 } else if (mActivatedHandle == mTopHandle || 867 mActivatedHandle == mBottomHandle) { 868 infoXPosition = aX + (aW / 2); 869 } else { 870 // should only occur when mActivatedHandle is one of the 3 right-side 871 // handles, but this is a reasonable default if it isn't any of them (?) 872 infoXPosition = aX + aW; 873 } 874 875 if (mActivatedHandle == mTopLeftHandle || mActivatedHandle == mTopHandle || 876 mActivatedHandle == mTopRightHandle) { 877 infoYPosition = aY; 878 } else if (mActivatedHandle == mLeftHandle || 879 mActivatedHandle == mRightHandle) { 880 infoYPosition = aY + (aH / 2); 881 } else { 882 // should only occur when mActivatedHandle is one of the 3 bottom-side 883 // handles, but this is a reasonable default if it isn't any of them (?) 884 infoYPosition = aY + aH; 885 } 886 887 // Offset info box by 20 so it's not directly under the mouse cursor. 888 const int mouseCursorOffset = 20; 889 if (RefPtr<nsStyledElement> resizingInfoStyledElement = 890 nsStyledElement::FromNodeOrNull(mResizingInfo.get())) { 891 nsresult rv; 892 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 893 *this, *resizingInfoStyledElement, *nsGkAtoms::left, 894 infoXPosition + mouseCursorOffset); 895 if (rv == NS_ERROR_EDITOR_DESTROYED) { 896 NS_WARNING( 897 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " 898 "destroyed the editor"); 899 return NS_ERROR_EDITOR_DESTROYED; 900 } 901 NS_WARNING_ASSERTION( 902 NS_SUCCEEDED(rv), 903 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " 904 "failed, but ignored"); 905 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 906 *this, *resizingInfoStyledElement, *nsGkAtoms::top, 907 infoYPosition + mouseCursorOffset); 908 if (rv == NS_ERROR_EDITOR_DESTROYED) { 909 NS_WARNING( 910 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 911 "destroyed the editor"); 912 return NS_ERROR_EDITOR_DESTROYED; 913 } 914 NS_WARNING_ASSERTION( 915 NS_SUCCEEDED(rv), 916 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 917 "failed, but ignored"); 918 } 919 920 nsCOMPtr<nsIContent> textInfo = mResizingInfo->GetFirstChild(); 921 ErrorResult error; 922 if (textInfo) { 923 mResizingInfo->RemoveChild(*textInfo, error); 924 if (error.Failed()) { 925 NS_WARNING("nsINode::RemoveChild() failed"); 926 return error.StealNSResult(); 927 } 928 textInfo = nullptr; 929 } 930 931 nsAutoString widthStr, heightStr, diffWidthStr, diffHeightStr; 932 widthStr.AppendInt(aW); 933 heightStr.AppendInt(aH); 934 int32_t diffWidth = aW - mResizedObjectWidth; 935 int32_t diffHeight = aH - mResizedObjectHeight; 936 if (diffWidth > 0) { 937 diffWidthStr.Assign('+'); 938 } 939 if (diffHeight > 0) { 940 diffHeightStr.Assign('+'); 941 } 942 diffWidthStr.AppendInt(diffWidth); 943 diffHeightStr.AppendInt(diffHeight); 944 945 nsAutoString info(widthStr + u" x "_ns + heightStr + u" ("_ns + diffWidthStr + 946 u", "_ns + diffHeightStr + u")"_ns); 947 948 RefPtr<Document> document = GetDocument(); 949 textInfo = document->CreateTextNode(info); 950 if (!textInfo) { 951 NS_WARNING("Document::CreateTextNode() failed"); 952 return NS_ERROR_FAILURE; 953 } 954 mResizingInfo->AppendChild(*textInfo, error); 955 if (error.Failed()) { 956 NS_WARNING("nsINode::AppendChild() failed"); 957 return error.StealNSResult(); 958 } 959 960 nsresult rv = 961 mResizingInfo->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true); 962 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 963 "Element::UnsetAttr(nsGkAtoms::hidden) failed"); 964 return rv; 965 } 966 967 nsresult HTMLEditor::SetShadowPosition(Element& aShadowElement, 968 Element& aElement, int32_t aElementX, 969 int32_t aElementY) { 970 MOZ_ASSERT(&aShadowElement == mResizingShadow || 971 &aShadowElement == mPositioningShadow); 972 RefPtr<Element> handlingShadowElement = &aShadowElement == mResizingShadow 973 ? mResizingShadow.get() 974 : mPositioningShadow.get(); 975 976 if (nsStyledElement* styledShadowElement = 977 nsStyledElement::FromNode(&aShadowElement)) { 978 // MOZ_KnownLive(*styledShadowElement): It's aShadowElement whose lifetime 979 // must be guaranteed by caller because of MOZ_CAN_RUN_SCRIPT method. 980 nsresult rv = SetAnonymousElementPositionWithoutTransaction( 981 MOZ_KnownLive(*styledShadowElement), aElementX, aElementY); 982 if (NS_FAILED(rv)) { 983 NS_WARNING( 984 "HTMLEditor::SetAnonymousElementPositionWithoutTransaction() " 985 "failed"); 986 return rv; 987 } 988 } 989 990 if (!HTMLEditUtils::IsImageElement(aElement)) { 991 return NS_OK; 992 } 993 994 nsAutoString imageSource; 995 aElement.GetAttr(nsGkAtoms::src, imageSource); 996 nsresult rv = aShadowElement.SetAttr(kNameSpaceID_None, nsGkAtoms::src, 997 imageSource, true); 998 if (NS_WARN_IF(Destroyed())) { 999 return NS_ERROR_EDITOR_DESTROYED; 1000 } 1001 if (NS_FAILED(rv)) { 1002 NS_WARNING("Element::SetAttr(nsGkAtoms::src) failed"); 1003 return NS_ERROR_FAILURE; 1004 } 1005 return NS_WARN_IF(&aShadowElement != handlingShadowElement) ? NS_ERROR_FAILURE 1006 : NS_OK; 1007 } 1008 1009 int32_t HTMLEditor::GetNewResizingIncrement(int32_t aX, int32_t aY, 1010 ResizeAt aResizeAt) const { 1011 int32_t result = 0; 1012 if (!mPreserveRatio) { 1013 switch (aResizeAt) { 1014 case ResizeAt::eX: 1015 case ResizeAt::eWidth: 1016 result = aX - mOriginalX; 1017 break; 1018 case ResizeAt::eY: 1019 case ResizeAt::eHeight: 1020 result = aY - mOriginalY; 1021 break; 1022 default: 1023 MOZ_ASSERT_UNREACHABLE("Invalid resizing request"); 1024 } 1025 return result; 1026 } 1027 1028 int32_t xi = (aX - mOriginalX) * mWidthIncrementFactor; 1029 int32_t yi = (aY - mOriginalY) * mHeightIncrementFactor; 1030 float objectSizeRatio = 1031 ((float)mResizedObjectWidth) / ((float)mResizedObjectHeight); 1032 result = (xi > yi) ? xi : yi; 1033 switch (aResizeAt) { 1034 case ResizeAt::eX: 1035 case ResizeAt::eWidth: 1036 if (result == yi) result = (int32_t)(((float)result) * objectSizeRatio); 1037 result = (int32_t)(((float)result) * mWidthIncrementFactor); 1038 break; 1039 case ResizeAt::eY: 1040 case ResizeAt::eHeight: 1041 if (result == xi) result = (int32_t)(((float)result) / objectSizeRatio); 1042 result = (int32_t)(((float)result) * mHeightIncrementFactor); 1043 break; 1044 } 1045 return result; 1046 } 1047 1048 int32_t HTMLEditor::GetNewResizingX(int32_t aX, int32_t aY) { 1049 int32_t resized = 1050 mResizedObjectX + 1051 GetNewResizingIncrement(aX, aY, ResizeAt::eX) * mXIncrementFactor; 1052 int32_t max = mResizedObjectX + mResizedObjectWidth; 1053 return std::min(resized, max); 1054 } 1055 1056 int32_t HTMLEditor::GetNewResizingY(int32_t aX, int32_t aY) { 1057 int32_t resized = 1058 mResizedObjectY + 1059 GetNewResizingIncrement(aX, aY, ResizeAt::eY) * mYIncrementFactor; 1060 int32_t max = mResizedObjectY + mResizedObjectHeight; 1061 return std::min(resized, max); 1062 } 1063 1064 int32_t HTMLEditor::GetNewResizingWidth(int32_t aX, int32_t aY) { 1065 int32_t resized = 1066 mResizedObjectWidth + 1067 GetNewResizingIncrement(aX, aY, ResizeAt::eWidth) * mWidthIncrementFactor; 1068 return std::max(resized, 1); 1069 } 1070 1071 int32_t HTMLEditor::GetNewResizingHeight(int32_t aX, int32_t aY) { 1072 int32_t resized = mResizedObjectHeight + 1073 GetNewResizingIncrement(aX, aY, ResizeAt::eHeight) * 1074 mHeightIncrementFactor; 1075 return std::max(resized, 1); 1076 } 1077 1078 nsresult HTMLEditor::UpdateResizerOrGrabberPositionTo( 1079 const CSSIntPoint& aClientPoint) { 1080 if (mIsResizing) { 1081 AutoEditActionDataSetter editActionData(*this, 1082 EditAction::eResizingElement); 1083 if (NS_WARN_IF(!editActionData.CanHandle())) { 1084 return NS_ERROR_NOT_INITIALIZED; 1085 } 1086 1087 // we are resizing and the mouse pointer's position has changed 1088 // we have to resdisplay the shadow 1089 const int32_t newX = GetNewResizingX(aClientPoint.x, aClientPoint.y); 1090 const int32_t newY = GetNewResizingY(aClientPoint.x, aClientPoint.y); 1091 const int32_t newWidth = 1092 GetNewResizingWidth(aClientPoint.x, aClientPoint.y); 1093 const int32_t newHeight = 1094 GetNewResizingHeight(aClientPoint.x, aClientPoint.y); 1095 1096 if (RefPtr<nsStyledElement> resizingShadowStyledElement = 1097 nsStyledElement::FromNodeOrNull(mResizingShadow.get())) { 1098 nsresult rv; 1099 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1100 *this, *resizingShadowStyledElement, *nsGkAtoms::left, newX); 1101 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1102 NS_WARNING( 1103 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left)" 1104 " destroyed the editor"); 1105 return NS_ERROR_EDITOR_DESTROYED; 1106 } 1107 NS_WARNING_ASSERTION( 1108 NS_SUCCEEDED(rv), 1109 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " 1110 "failed, but ignored"); 1111 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1112 *this, *resizingShadowStyledElement, *nsGkAtoms::top, newY); 1113 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1114 NS_WARNING( 1115 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top)" 1116 " destroyed the editor"); 1117 return NS_ERROR_EDITOR_DESTROYED; 1118 } 1119 NS_WARNING_ASSERTION( 1120 NS_SUCCEEDED(rv), 1121 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 1122 "failed, but ignored"); 1123 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1124 *this, *resizingShadowStyledElement, *nsGkAtoms::width, newWidth); 1125 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1126 NS_WARNING( 1127 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1128 "width) destroyed the editor"); 1129 return NS_ERROR_EDITOR_DESTROYED; 1130 } 1131 NS_WARNING_ASSERTION( 1132 NS_SUCCEEDED(rv), 1133 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::width) " 1134 "failed, but ignored"); 1135 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1136 *this, *resizingShadowStyledElement, *nsGkAtoms::height, newHeight); 1137 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1138 NS_WARNING( 1139 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1140 "height) destroyed the editor"); 1141 return NS_ERROR_EDITOR_DESTROYED; 1142 } 1143 NS_WARNING_ASSERTION( 1144 NS_SUCCEEDED(rv), 1145 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::height)" 1146 " failed, but ignored"); 1147 } 1148 1149 nsresult rv = SetResizingInfoPosition(newX, newY, newWidth, newHeight); 1150 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), 1151 "HTMLEditor::SetResizingInfoPosition() failed"); 1152 return rv; 1153 } 1154 1155 AutoEditActionDataSetter editActionData(*this, EditAction::eMovingElement); 1156 if (NS_WARN_IF(!editActionData.CanHandle())) { 1157 return NS_ERROR_NOT_INITIALIZED; 1158 } 1159 1160 if (mGrabberClicked) { 1161 int32_t xThreshold = 1162 LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdX, 1); 1163 int32_t yThreshold = 1164 LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdY, 1); 1165 1166 if (DeprecatedAbs(aClientPoint.x - mOriginalX) * 2 >= xThreshold || 1167 DeprecatedAbs(aClientPoint.y - mOriginalY) * 2 >= yThreshold) { 1168 mGrabberClicked = false; 1169 DebugOnly<nsresult> rvIgnored = StartMoving(); 1170 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored), 1171 "HTMLEditor::StartMoving() failed, but ignored"); 1172 } 1173 } 1174 if (mIsMoving) { 1175 int32_t newX = mPositionedObjectX + aClientPoint.x - mOriginalX; 1176 int32_t newY = mPositionedObjectY + aClientPoint.y - mOriginalY; 1177 1178 // Maybe align newX and newY to the grid. 1179 SnapToGrid(newX, newY); 1180 1181 if (RefPtr<nsStyledElement> positioningShadowStyledElement = 1182 nsStyledElement::FromNodeOrNull(mPositioningShadow.get())) { 1183 nsresult rv; 1184 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1185 *this, *positioningShadowStyledElement, *nsGkAtoms::left, newX); 1186 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1187 NS_WARNING( 1188 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left)" 1189 " destroyed the editor"); 1190 return NS_ERROR_EDITOR_DESTROYED; 1191 } 1192 NS_WARNING_ASSERTION( 1193 NS_SUCCEEDED(rv), 1194 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " 1195 "failed, but ignored"); 1196 rv = CSSEditUtils::SetCSSPropertyPixelsWithoutTransaction( 1197 *this, *positioningShadowStyledElement, *nsGkAtoms::top, newY); 1198 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1199 NS_WARNING( 1200 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 1201 "destroyed the editor"); 1202 return NS_ERROR_EDITOR_DESTROYED; 1203 } 1204 NS_WARNING_ASSERTION( 1205 NS_SUCCEEDED(rv), 1206 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 1207 "failed, but ignored"); 1208 } 1209 } 1210 return NS_OK; 1211 } 1212 1213 nsresult HTMLEditor::SetFinalSizeWithTransaction(int32_t aX, int32_t aY) { 1214 if (!mResizedObject) { 1215 // paranoia 1216 return NS_OK; 1217 } 1218 1219 if (mActivatedHandle) { 1220 mActivatedHandle->ClassList()->Remove(u"active"_ns, IgnoreErrors()); 1221 mActivatedHandle = nullptr; 1222 } 1223 1224 // we have now to set the new width and height of the resized object 1225 // we don't set the x and y position because we don't control that in 1226 // a normal HTML layout 1227 int32_t left = GetNewResizingX(aX, aY); 1228 int32_t top = GetNewResizingY(aX, aY); 1229 int32_t width = GetNewResizingWidth(aX, aY); 1230 int32_t height = GetNewResizingHeight(aX, aY); 1231 bool setWidth = 1232 !mResizedObjectIsAbsolutelyPositioned || (width != mResizedObjectWidth); 1233 bool setHeight = 1234 !mResizedObjectIsAbsolutelyPositioned || (height != mResizedObjectHeight); 1235 1236 int32_t x, y; 1237 x = left - ((mResizedObjectIsAbsolutelyPositioned) 1238 ? mResizedObjectBorderLeft + mResizedObjectMarginLeft 1239 : 0); 1240 y = top - ((mResizedObjectIsAbsolutelyPositioned) 1241 ? mResizedObjectBorderTop + mResizedObjectMarginTop 1242 : 0); 1243 1244 // we want one transaction only from a user's point of view 1245 AutoPlaceholderBatch treatAsOneTransaction( 1246 *this, ScrollSelectionIntoView::Yes, __FUNCTION__); 1247 RefPtr<Element> resizedElement(mResizedObject); 1248 RefPtr<nsStyledElement> resizedStyleElement = 1249 nsStyledElement::FromNodeOrNull(mResizedObject); 1250 1251 if (mResizedObjectIsAbsolutelyPositioned && resizedStyleElement) { 1252 if (setHeight) { 1253 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1254 *this, *resizedStyleElement, *nsGkAtoms::top, y); 1255 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1256 NS_WARNING( 1257 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1258 "top) destoyed the editor"); 1259 return NS_ERROR_EDITOR_DESTROYED; 1260 } 1261 NS_WARNING_ASSERTION( 1262 NS_SUCCEEDED(rv), 1263 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::top) " 1264 "failed, but ignored"); 1265 } 1266 if (setWidth) { 1267 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1268 *this, *resizedStyleElement, *nsGkAtoms::left, x); 1269 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1270 NS_WARNING( 1271 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1272 "left) destoyed the editor"); 1273 return NS_ERROR_EDITOR_DESTROYED; 1274 } 1275 NS_WARNING_ASSERTION( 1276 NS_SUCCEEDED(rv), 1277 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::left) " 1278 "failed, but ignored"); 1279 } 1280 } 1281 if (IsCSSEnabled() || mResizedObjectIsAbsolutelyPositioned) { 1282 if (setWidth && resizedElement->HasAttr(nsGkAtoms::width)) { 1283 nsresult rv = 1284 RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::width); 1285 if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) { 1286 NS_WARNING( 1287 "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) " 1288 "failed"); 1289 return NS_ERROR_EDITOR_DESTROYED; 1290 } 1291 NS_WARNING_ASSERTION( 1292 NS_SUCCEEDED(rv), 1293 "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::width) " 1294 "failed, but ignored"); 1295 } 1296 1297 if (setHeight && resizedElement->HasAttr(nsGkAtoms::height)) { 1298 nsresult rv = 1299 RemoveAttributeWithTransaction(*resizedElement, *nsGkAtoms::height); 1300 if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) { 1301 NS_WARNING( 1302 "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) " 1303 "failed"); 1304 return NS_ERROR_EDITOR_DESTROYED; 1305 } 1306 NS_WARNING_ASSERTION( 1307 NS_SUCCEEDED(rv), 1308 "EditorBase::RemoveAttributeWithTransaction(nsGkAtoms::height) " 1309 "failed, but ignored"); 1310 } 1311 1312 if (resizedStyleElement) { 1313 if (setWidth) { 1314 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1315 *this, *resizedStyleElement, *nsGkAtoms::width, width); 1316 if (NS_FAILED(rv)) { 1317 NS_WARNING( 1318 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1319 "width) destoyed the editor"); 1320 return NS_ERROR_EDITOR_DESTROYED; 1321 } 1322 NS_WARNING_ASSERTION( 1323 NS_SUCCEEDED(rv), 1324 "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::width) " 1325 "failed, but ignored"); 1326 } 1327 if (setHeight) { 1328 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1329 *this, *resizedStyleElement, *nsGkAtoms::height, height); 1330 if (NS_FAILED(rv)) { 1331 NS_WARNING( 1332 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1333 "height) destoyed the editor"); 1334 return NS_ERROR_EDITOR_DESTROYED; 1335 } 1336 NS_WARNING_ASSERTION( 1337 NS_SUCCEEDED(rv), 1338 "CSSEditUtils::SetCSSPropertyPixels(nsGkAtoms::height) " 1339 "failed, but ignored"); 1340 } 1341 } 1342 } else { 1343 // we use HTML size and remove all equivalent CSS properties 1344 1345 // we set the CSS width and height to remove it later, 1346 // triggering an immediate reflow; otherwise, we have problems 1347 // with asynchronous reflow 1348 if (resizedStyleElement) { 1349 if (setWidth) { 1350 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1351 *this, *resizedStyleElement, *nsGkAtoms::width, width); 1352 if (NS_FAILED(rv)) { 1353 NS_WARNING( 1354 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1355 "width) destoyed the editor"); 1356 return NS_ERROR_EDITOR_DESTROYED; 1357 } 1358 NS_WARNING_ASSERTION( 1359 NS_SUCCEEDED(rv), 1360 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1361 "width) failed, but ignored"); 1362 } 1363 if (setHeight) { 1364 nsresult rv = CSSEditUtils::SetCSSPropertyPixelsWithTransaction( 1365 *this, *resizedStyleElement, *nsGkAtoms::height, height); 1366 if (NS_FAILED(rv)) { 1367 NS_WARNING( 1368 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1369 "height) destoyed the editor"); 1370 return NS_ERROR_EDITOR_DESTROYED; 1371 } 1372 NS_WARNING_ASSERTION( 1373 NS_SUCCEEDED(rv), 1374 "CSSEditUtils::SetCSSPropertyPixelsWithTransaction(nsGkAtoms::" 1375 "height) failed, but ignored"); 1376 } 1377 } 1378 if (setWidth) { 1379 nsAutoString w; 1380 w.AppendInt(width); 1381 DebugOnly<nsresult> rvIgnored = 1382 SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::width, w); 1383 if (NS_WARN_IF(Destroyed())) { 1384 return NS_ERROR_EDITOR_DESTROYED; 1385 } 1386 NS_WARNING_ASSERTION( 1387 NS_SUCCEEDED(rvIgnored), 1388 "EditorBase::SetAttributeWithTransaction(nsGkAtoms::width) " 1389 "failed, but ignored"); 1390 } 1391 if (setHeight) { 1392 nsAutoString h; 1393 h.AppendInt(height); 1394 DebugOnly<nsresult> rvIgnored = 1395 SetAttributeWithTransaction(*resizedElement, *nsGkAtoms::height, h); 1396 if (NS_WARN_IF(Destroyed())) { 1397 return NS_ERROR_EDITOR_DESTROYED; 1398 } 1399 NS_WARNING_ASSERTION( 1400 NS_SUCCEEDED(rvIgnored), 1401 "EditorBase::SetAttributeWithTransaction(nsGkAtoms::height) " 1402 "failed, but ignored"); 1403 } 1404 1405 if (resizedStyleElement) { 1406 if (setWidth) { 1407 nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction( 1408 *this, *resizedStyleElement, *nsGkAtoms::width, u""_ns); 1409 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1410 NS_WARNING( 1411 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width)" 1412 " destroyed the editor"); 1413 return NS_ERROR_EDITOR_DESTROYED; 1414 } 1415 NS_WARNING_ASSERTION( 1416 NS_SUCCEEDED(rv), 1417 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::width) " 1418 "failed, but ignored"); 1419 } 1420 if (setHeight) { 1421 nsresult rv = CSSEditUtils::RemoveCSSPropertyWithTransaction( 1422 *this, *resizedStyleElement, *nsGkAtoms::height, u""_ns); 1423 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1424 NS_WARNING( 1425 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::" 1426 "height) destroyed the editor"); 1427 return NS_ERROR_EDITOR_DESTROYED; 1428 } 1429 NS_WARNING_ASSERTION( 1430 NS_SUCCEEDED(rv), 1431 "CSSEditUtils::RemoveCSSPropertyWithTransaction(nsGkAtoms::height) " 1432 "failed, but ignored"); 1433 } 1434 } 1435 } 1436 1437 // keep track of that size 1438 mResizedObjectWidth = width; 1439 mResizedObjectHeight = height; 1440 1441 nsresult rv = RefreshResizersInternal(); 1442 if (rv == NS_ERROR_EDITOR_DESTROYED) { 1443 return NS_ERROR_EDITOR_DESTROYED; 1444 } 1445 NS_WARNING_ASSERTION( 1446 NS_SUCCEEDED(rv), 1447 "HTMLEditor::RefreshResizersInternal() failed, but ignored"); 1448 return NS_OK; 1449 } 1450 1451 NS_IMETHODIMP HTMLEditor::GetObjectResizingEnabled( 1452 bool* aIsObjectResizingEnabled) { 1453 *aIsObjectResizingEnabled = IsObjectResizerEnabled(); 1454 return NS_OK; 1455 } 1456 1457 NS_IMETHODIMP HTMLEditor::SetObjectResizingEnabled( 1458 bool aObjectResizingEnabled) { 1459 EnableObjectResizer(aObjectResizingEnabled); 1460 return NS_OK; 1461 } 1462 1463 NS_IMETHODIMP HTMLEditor::GetIsObjectResizingActive(bool* aIsActive) { 1464 MOZ_ASSERT(aIsActive); 1465 *aIsActive = !!mResizedObject; 1466 return NS_OK; 1467 } 1468 1469 #undef kTopLeft 1470 #undef kTop 1471 #undef kTopRight 1472 #undef kLeft 1473 #undef kRight 1474 #undef kBottomLeft 1475 #undef kBottom 1476 #undef kBottomRight 1477 1478 } // namespace mozilla