/src/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/HTMLEditor.h" |
6 | | |
7 | | #include <math.h> |
8 | | |
9 | | #include "HTMLEditorObjectResizerUtils.h" |
10 | | #include "HTMLEditRules.h" |
11 | | #include "HTMLEditUtils.h" |
12 | | #include "TextEditUtils.h" |
13 | | #include "mozilla/EditAction.h" |
14 | | #include "mozilla/EditorUtils.h" |
15 | | #include "mozilla/Preferences.h" |
16 | | #include "mozilla/TextEditRules.h" |
17 | | #include "mozilla/dom/Selection.h" |
18 | | #include "mozilla/dom/Element.h" |
19 | | #include "mozilla/dom/EventTarget.h" |
20 | | #include "mozilla/mozalloc.h" |
21 | | #include "nsAString.h" |
22 | | #include "nsAlgorithm.h" |
23 | | #include "nsCOMPtr.h" |
24 | | #include "nsComputedDOMStyle.h" |
25 | | #include "nsDebug.h" |
26 | | #include "nsError.h" |
27 | | #include "nsGkAtoms.h" |
28 | | #include "nsIContent.h" |
29 | | #include "nsROCSSPrimitiveValue.h" |
30 | | #include "nsIDOMEventListener.h" |
31 | | #include "nsDOMCSSRGBColor.h" |
32 | | #include "nsIDOMWindow.h" |
33 | | #include "nsIHTMLObjectResizer.h" |
34 | | #include "nsINode.h" |
35 | | #include "nsIPresShell.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 "nscore.h" |
43 | | #include <algorithm> |
44 | | |
45 | | namespace mozilla { |
46 | | |
47 | | using namespace dom; |
48 | | |
49 | | nsresult |
50 | | HTMLEditor::SetSelectionToAbsoluteOrStatic(bool aEnabled) |
51 | 0 | { |
52 | 0 | AutoPlaceholderBatch beginBatching(this); |
53 | 0 | AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction( |
54 | 0 | *this, |
55 | 0 | aEnabled ? |
56 | 0 | EditSubAction::eSetPositionToAbsolute : |
57 | 0 | EditSubAction::eSetPositionToStatic, |
58 | 0 | nsIEditor::eNext); |
59 | 0 |
|
60 | 0 | // the line below does not match the code; should it be removed? |
61 | 0 | // Find out if the selection is collapsed: |
62 | 0 | RefPtr<Selection> selection = GetSelection(); |
63 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
64 | 0 |
|
65 | 0 | EditSubActionInfo subActionInfo( |
66 | 0 | aEnabled ? EditSubAction::eSetPositionToAbsolute : |
67 | 0 | EditSubAction::eSetPositionToStatic); |
68 | 0 | bool cancel, handled; |
69 | 0 | // Protect the edit rules object from dying |
70 | 0 | RefPtr<TextEditRules> rules(mRules); |
71 | 0 | nsresult rv = |
72 | 0 | rules->WillDoAction(selection, subActionInfo, &cancel, &handled); |
73 | 0 | if (NS_FAILED(rv) || cancel) { |
74 | 0 | return rv; |
75 | 0 | } |
76 | 0 | |
77 | 0 | return rules->DidDoAction(selection, subActionInfo, rv); |
78 | 0 | } |
79 | | |
80 | | already_AddRefed<Element> |
81 | | HTMLEditor::GetAbsolutelyPositionedSelectionContainer() |
82 | 0 | { |
83 | 0 | RefPtr<Selection> selection = GetSelection(); |
84 | 0 | if (NS_WARN_IF(!selection)) { |
85 | 0 | return nullptr; |
86 | 0 | } |
87 | 0 | |
88 | 0 | RefPtr<Element> element = GetSelectionContainerElement(*selection); |
89 | 0 | if (NS_WARN_IF(!element)) { |
90 | 0 | return nullptr; |
91 | 0 | } |
92 | 0 | |
93 | 0 | nsAutoString positionStr; |
94 | 0 | while (element && !element->IsHTMLElement(nsGkAtoms::html)) { |
95 | 0 | nsresult rv = |
96 | 0 | CSSEditUtils::GetComputedProperty(*element, *nsGkAtoms::position, |
97 | 0 | positionStr); |
98 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
99 | 0 | return nullptr; |
100 | 0 | } |
101 | 0 | if (positionStr.EqualsLiteral("absolute")) { |
102 | 0 | return element.forget(); |
103 | 0 | } |
104 | 0 | element = element->GetParentElement(); |
105 | 0 | } |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | | |
109 | | NS_IMETHODIMP |
110 | | HTMLEditor::GetAbsolutePositioningEnabled(bool* aIsEnabled) |
111 | 0 | { |
112 | 0 | *aIsEnabled = IsAbsolutePositionEditorEnabled(); |
113 | 0 | return NS_OK; |
114 | 0 | } |
115 | | |
116 | | NS_IMETHODIMP |
117 | | HTMLEditor::SetAbsolutePositioningEnabled(bool aIsEnabled) |
118 | 0 | { |
119 | 0 | EnableAbsolutePositionEditor(aIsEnabled); |
120 | 0 | return NS_OK; |
121 | 0 | } |
122 | | |
123 | | nsresult |
124 | | HTMLEditor::RelativeChangeElementZIndex(Element& aElement, |
125 | | int32_t aChange, |
126 | | int32_t* aReturn) |
127 | 0 | { |
128 | 0 | NS_ENSURE_ARG_POINTER(aReturn); |
129 | 0 | if (!aChange) // early way out, no change |
130 | 0 | return NS_OK; |
131 | 0 | |
132 | 0 | int32_t zIndex = GetZIndex(aElement); |
133 | 0 | zIndex = std::max(zIndex + aChange, 0); |
134 | 0 | SetZIndex(aElement, zIndex); |
135 | 0 | *aReturn = zIndex; |
136 | 0 |
|
137 | 0 | return NS_OK; |
138 | 0 | } |
139 | | |
140 | | void |
141 | | HTMLEditor::SetZIndex(Element& aElement, |
142 | | int32_t aZindex) |
143 | 0 | { |
144 | 0 | nsAutoString zIndexStr; |
145 | 0 | zIndexStr.AppendInt(aZindex); |
146 | 0 |
|
147 | 0 | mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::z_index, zIndexStr); |
148 | 0 | } |
149 | | |
150 | | nsresult |
151 | | HTMLEditor::AddZIndex(int32_t aChange) |
152 | 0 | { |
153 | 0 | AutoPlaceholderBatch beginBatching(this); |
154 | 0 | AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction( |
155 | 0 | *this, |
156 | 0 | aChange < 0 ? |
157 | 0 | EditSubAction::eDecreaseZIndex : |
158 | 0 | EditSubAction::eIncreaseZIndex, |
159 | 0 | nsIEditor::eNext); |
160 | 0 |
|
161 | 0 | // brade: can we get rid of this comment? |
162 | 0 | // Find out if the selection is collapsed: |
163 | 0 | RefPtr<Selection> selection = GetSelection(); |
164 | 0 | NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); |
165 | 0 | EditSubActionInfo subActionInfo(aChange < 0 ? EditSubAction::eDecreaseZIndex : |
166 | 0 | EditSubAction::eIncreaseZIndex); |
167 | 0 | bool cancel, handled; |
168 | 0 | // Protect the edit rules object from dying |
169 | 0 | RefPtr<TextEditRules> rules(mRules); |
170 | 0 | nsresult rv = |
171 | 0 | rules->WillDoAction(selection, subActionInfo, &cancel, &handled); |
172 | 0 | if (cancel || NS_FAILED(rv)) { |
173 | 0 | return rv; |
174 | 0 | } |
175 | 0 | |
176 | 0 | return rules->DidDoAction(selection, subActionInfo, rv); |
177 | 0 | } |
178 | | |
179 | | int32_t |
180 | | HTMLEditor::GetZIndex(Element& aElement) |
181 | 0 | { |
182 | 0 | nsAutoString zIndexStr; |
183 | 0 |
|
184 | 0 | nsresult rv = |
185 | 0 | CSSEditUtils::GetSpecifiedProperty(aElement, *nsGkAtoms::z_index, |
186 | 0 | zIndexStr); |
187 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
188 | 0 | return 0; |
189 | 0 | } |
190 | 0 | if (zIndexStr.EqualsLiteral("auto")) { |
191 | 0 | // we have to look at the positioned ancestors |
192 | 0 | // cf. CSS 2 spec section 9.9.1 |
193 | 0 | nsCOMPtr<nsINode> node = aElement.GetParentNode(); |
194 | 0 | nsAutoString positionStr; |
195 | 0 | while (node && zIndexStr.EqualsLiteral("auto") && |
196 | 0 | !node->IsHTMLElement(nsGkAtoms::body)) { |
197 | 0 | rv = CSSEditUtils::GetComputedProperty(*node, *nsGkAtoms::position, |
198 | 0 | positionStr); |
199 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
200 | 0 | return 0; |
201 | 0 | } |
202 | 0 | if (positionStr.EqualsLiteral("absolute")) { |
203 | 0 | // ah, we found one, what's its z-index ? If its z-index is auto, |
204 | 0 | // we have to continue climbing the document's tree |
205 | 0 | rv = CSSEditUtils::GetComputedProperty(*node, *nsGkAtoms::z_index, |
206 | 0 | zIndexStr); |
207 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
208 | 0 | return 0; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | node = node->GetParentNode(); |
212 | 0 | } |
213 | 0 | } |
214 | 0 |
|
215 | 0 | if (zIndexStr.EqualsLiteral("auto")) { |
216 | 0 | return 0; |
217 | 0 | } |
218 | 0 | |
219 | 0 | nsresult errorCode; |
220 | 0 | return zIndexStr.ToInteger(&errorCode); |
221 | 0 | } |
222 | | |
223 | | bool |
224 | | HTMLEditor::CreateGrabberInternal(nsIContent& aParentContent) |
225 | 0 | { |
226 | 0 | if (NS_WARN_IF(mGrabber)) { |
227 | 0 | return false; |
228 | 0 | } |
229 | 0 | |
230 | 0 | mGrabber = CreateAnonymousElement(nsGkAtoms::span, aParentContent, |
231 | 0 | NS_LITERAL_STRING("mozGrabber"), false); |
232 | 0 |
|
233 | 0 | // mGrabber may be destroyed during creation due to there may be |
234 | 0 | // mutation event listener. |
235 | 0 | if (NS_WARN_IF(!mGrabber)) { |
236 | 0 | return false; |
237 | 0 | } |
238 | 0 | |
239 | 0 | EventListenerManager* eventListenerManager = |
240 | 0 | mGrabber->GetOrCreateListenerManager(); |
241 | 0 | eventListenerManager->AddEventListenerByType( |
242 | 0 | mEventListener, |
243 | 0 | NS_LITERAL_STRING("mousedown"), |
244 | 0 | TrustedEventsAtSystemGroupBubble()); |
245 | 0 | MOZ_ASSERT(mGrabber); |
246 | 0 | return true; |
247 | 0 | } |
248 | | |
249 | | NS_IMETHODIMP |
250 | | HTMLEditor::RefreshGrabber() |
251 | 0 | { |
252 | 0 | if (NS_WARN_IF(!mAbsolutelyPositionedObject)) { |
253 | 0 | return NS_ERROR_FAILURE; |
254 | 0 | } |
255 | 0 | |
256 | 0 | nsresult rv = RefreshGrabberInternal(); |
257 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
258 | 0 | return rv; |
259 | 0 | } |
260 | 0 | return NS_OK; |
261 | 0 | } |
262 | | |
263 | | nsresult |
264 | | HTMLEditor::RefreshGrabberInternal() |
265 | 0 | { |
266 | 0 | if (!mAbsolutelyPositionedObject) { |
267 | 0 | return NS_OK; |
268 | 0 | } |
269 | 0 | nsresult rv = GetPositionAndDimensions(*mAbsolutelyPositionedObject, |
270 | 0 | mPositionedObjectX, |
271 | 0 | mPositionedObjectY, |
272 | 0 | mPositionedObjectWidth, |
273 | 0 | mPositionedObjectHeight, |
274 | 0 | mPositionedObjectBorderLeft, |
275 | 0 | mPositionedObjectBorderTop, |
276 | 0 | mPositionedObjectMarginLeft, |
277 | 0 | mPositionedObjectMarginTop); |
278 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
279 | 0 | return rv; |
280 | 0 | } |
281 | 0 | |
282 | 0 | SetAnonymousElementPosition(mPositionedObjectX + 12, |
283 | 0 | mPositionedObjectY - 14, |
284 | 0 | mGrabber); |
285 | 0 | return NS_OK; |
286 | 0 | } |
287 | | |
288 | | void |
289 | | HTMLEditor::HideGrabberInternal() |
290 | 0 | { |
291 | 0 | if (NS_WARN_IF(!mAbsolutelyPositionedObject)) { |
292 | 0 | return; |
293 | 0 | } |
294 | 0 | |
295 | 0 | // Move all members to the local variables first since mutation event |
296 | 0 | // listener may try to show grabber while we're hiding them. |
297 | 0 | RefPtr<Element> absolutePositioningObject = |
298 | 0 | std::move(mAbsolutelyPositionedObject); |
299 | 0 | ManualNACPtr grabber = std::move(mGrabber); |
300 | 0 | ManualNACPtr positioningShadow = std::move(mPositioningShadow); |
301 | 0 |
|
302 | 0 | DebugOnly<nsresult> rv = |
303 | 0 | absolutePositioningObject->UnsetAttr(kNameSpaceID_None, |
304 | 0 | nsGkAtoms::_moz_abspos, |
305 | 0 | true); |
306 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to unset the attribute"); |
307 | 0 |
|
308 | 0 | // We allow the pres shell to be null; when it is, we presume there |
309 | 0 | // are no document observers to notify, but we still want to |
310 | 0 | // UnbindFromTree. |
311 | 0 | nsCOMPtr<nsIPresShell> presShell = GetPresShell(); |
312 | 0 | if (grabber) { |
313 | 0 | DeleteRefToAnonymousNode(std::move(grabber), presShell); |
314 | 0 | } |
315 | 0 | if (positioningShadow) { |
316 | 0 | DeleteRefToAnonymousNode(std::move(positioningShadow), presShell); |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | nsresult |
321 | | HTMLEditor::ShowGrabberInternal(Element& aElement) |
322 | 0 | { |
323 | 0 | if (NS_WARN_IF(!IsDescendantOfEditorRoot(&aElement))) { |
324 | 0 | return NS_ERROR_UNEXPECTED; |
325 | 0 | } |
326 | 0 | |
327 | 0 | if (NS_WARN_IF(mGrabber)) { |
328 | 0 | return NS_ERROR_UNEXPECTED; |
329 | 0 | } |
330 | 0 | |
331 | 0 | nsAutoString classValue; |
332 | 0 | nsresult rv = |
333 | 0 | GetTemporaryStyleForFocusedPositionedElement(aElement, classValue); |
334 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
335 | 0 |
|
336 | 0 | rv = aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos, |
337 | 0 | classValue, true); |
338 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
339 | 0 |
|
340 | 0 | mAbsolutelyPositionedObject = &aElement; |
341 | 0 |
|
342 | 0 | nsIContent* parentContent = aElement.GetParent(); |
343 | 0 | if (NS_WARN_IF(!parentContent)) { |
344 | 0 | return NS_ERROR_FAILURE; |
345 | 0 | } |
346 | 0 | |
347 | 0 | if (NS_WARN_IF(!CreateGrabberInternal(*parentContent))) { |
348 | 0 | return NS_ERROR_FAILURE; |
349 | 0 | } |
350 | 0 | |
351 | 0 | // If we succeeded to create the grabber, HideGrabberInternal() hasn't been |
352 | 0 | // called yet. So, mAbsolutelyPositionedObject should be non-nullptr. |
353 | 0 | MOZ_ASSERT(mAbsolutelyPositionedObject); |
354 | 0 |
|
355 | 0 | mHasShownGrabber = true; |
356 | 0 |
|
357 | 0 | // Finally, move the grabber to proper position. |
358 | 0 | rv = RefreshGrabberInternal(); |
359 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
360 | 0 | return rv; |
361 | 0 | } |
362 | 0 | return NS_OK; |
363 | 0 | } |
364 | | |
365 | | nsresult |
366 | | HTMLEditor::StartMoving() |
367 | 0 | { |
368 | 0 | nsCOMPtr<nsIContent> parentContent = mGrabber->GetParent(); |
369 | 0 | if (NS_WARN_IF(!parentContent) || NS_WARN_IF(!mAbsolutelyPositionedObject)) { |
370 | 0 | return NS_ERROR_FAILURE; |
371 | 0 | } |
372 | 0 | |
373 | 0 | // now, let's create the resizing shadow |
374 | 0 | mPositioningShadow = |
375 | 0 | CreateShadow(*parentContent, *mAbsolutelyPositionedObject); |
376 | 0 | if (NS_WARN_IF(!mPositioningShadow) || |
377 | 0 | NS_WARN_IF(!mAbsolutelyPositionedObject)) { |
378 | 0 | return NS_ERROR_FAILURE; |
379 | 0 | } |
380 | 0 | nsresult rv = SetShadowPosition(*mPositioningShadow, |
381 | 0 | *mAbsolutelyPositionedObject, |
382 | 0 | mPositionedObjectX, mPositionedObjectY); |
383 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
384 | 0 |
|
385 | 0 | // make the shadow appear |
386 | 0 | mPositioningShadow->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class, true); |
387 | 0 |
|
388 | 0 | // position it |
389 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::width, |
390 | 0 | mPositionedObjectWidth); |
391 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*mPositioningShadow, *nsGkAtoms::height, |
392 | 0 | mPositionedObjectHeight); |
393 | 0 |
|
394 | 0 | mIsMoving = true; |
395 | 0 | return NS_OK; // XXX Looks like nobody refers this result |
396 | 0 | } |
397 | | |
398 | | void |
399 | | HTMLEditor::SnapToGrid(int32_t& newX, int32_t& newY) |
400 | 0 | { |
401 | 0 | if (mSnapToGridEnabled && mGridSize) { |
402 | 0 | newX = (int32_t) floor( ((float)newX / (float)mGridSize) + 0.5f ) * mGridSize; |
403 | 0 | newY = (int32_t) floor( ((float)newY / (float)mGridSize) + 0.5f ) * mGridSize; |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | nsresult |
408 | | HTMLEditor::GrabberClicked() |
409 | 0 | { |
410 | 0 | // add a mouse move listener to the editor |
411 | 0 | nsresult rv = NS_OK; |
412 | 0 | if (!mMouseMotionListenerP) { |
413 | 0 | EventTarget* eventTarget = GetDOMEventTarget(); |
414 | 0 | if (NS_WARN_IF(!eventTarget)) { |
415 | 0 | return NS_ERROR_FAILURE; |
416 | 0 | } |
417 | 0 | mMouseMotionListenerP = new ResizerMouseMotionListener(*this); |
418 | 0 | EventListenerManager* eventListenerManager = |
419 | 0 | eventTarget->GetOrCreateListenerManager(); |
420 | 0 | eventListenerManager->AddEventListenerByType( |
421 | 0 | mMouseMotionListenerP, |
422 | 0 | NS_LITERAL_STRING("mousemove"), |
423 | 0 | TrustedEventsAtSystemGroupBubble()); |
424 | 0 | } |
425 | 0 | mGrabberClicked = true; |
426 | 0 | return rv; |
427 | 0 | } |
428 | | |
429 | | nsresult |
430 | | HTMLEditor::EndMoving() |
431 | 0 | { |
432 | 0 | if (mPositioningShadow) { |
433 | 0 | nsCOMPtr<nsIPresShell> ps = GetPresShell(); |
434 | 0 | NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED); |
435 | 0 |
|
436 | 0 | DeleteRefToAnonymousNode(std::move(mPositioningShadow), ps); |
437 | 0 |
|
438 | 0 | mPositioningShadow = nullptr; |
439 | 0 | } |
440 | 0 |
|
441 | 0 | EventTarget* eventTarget = GetDOMEventTarget(); |
442 | 0 | if (eventTarget && mMouseMotionListenerP) { |
443 | 0 | EventListenerManager* eventListenerManager = |
444 | 0 | eventTarget->GetOrCreateListenerManager(); |
445 | 0 | eventListenerManager->RemoveEventListenerByType( |
446 | 0 | mMouseMotionListenerP, |
447 | 0 | NS_LITERAL_STRING("mousemove"), |
448 | 0 | TrustedEventsAtSystemGroupBubble()); |
449 | 0 | } |
450 | 0 | mMouseMotionListenerP = nullptr; |
451 | 0 |
|
452 | 0 | mGrabberClicked = false; |
453 | 0 | mIsMoving = false; |
454 | 0 | RefPtr<Selection> selection = GetSelection(); |
455 | 0 | if (!selection) { |
456 | 0 | return NS_ERROR_NOT_INITIALIZED; |
457 | 0 | } |
458 | 0 | nsresult rv = RefereshEditingUI(*selection); |
459 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
460 | 0 | return rv; |
461 | 0 | } |
462 | 0 | return NS_OK; |
463 | 0 | } |
464 | | nsresult |
465 | | HTMLEditor::SetFinalPosition(int32_t aX, |
466 | | int32_t aY) |
467 | 0 | { |
468 | 0 | nsresult rv = EndMoving(); |
469 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
470 | 0 |
|
471 | 0 | // we have now to set the new width and height of the resized object |
472 | 0 | // we don't set the x and y position because we don't control that in |
473 | 0 | // a normal HTML layout |
474 | 0 | int32_t newX = mPositionedObjectX + aX - mOriginalX - (mPositionedObjectBorderLeft+mPositionedObjectMarginLeft); |
475 | 0 | int32_t newY = mPositionedObjectY + aY - mOriginalY - (mPositionedObjectBorderTop+mPositionedObjectMarginTop); |
476 | 0 |
|
477 | 0 | SnapToGrid(newX, newY); |
478 | 0 |
|
479 | 0 | nsAutoString x, y; |
480 | 0 | x.AppendInt(newX); |
481 | 0 | y.AppendInt(newY); |
482 | 0 |
|
483 | 0 | // we want one transaction only from a user's point of view |
484 | 0 | AutoPlaceholderBatch batchIt(this); |
485 | 0 |
|
486 | 0 | if (NS_WARN_IF(!mAbsolutelyPositionedObject)) { |
487 | 0 | return NS_ERROR_FAILURE; |
488 | 0 | } |
489 | 0 | OwningNonNull<Element> absolutelyPositionedObject = |
490 | 0 | *mAbsolutelyPositionedObject; |
491 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*absolutelyPositionedObject, |
492 | 0 | *nsGkAtoms::top, newY); |
493 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*absolutelyPositionedObject, |
494 | 0 | *nsGkAtoms::left, newX); |
495 | 0 | // keep track of that size |
496 | 0 | mPositionedObjectX = newX; |
497 | 0 | mPositionedObjectY = newY; |
498 | 0 |
|
499 | 0 | rv = RefreshResizers(); |
500 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
501 | 0 | return rv; |
502 | 0 | } |
503 | 0 | return NS_OK; |
504 | 0 | } |
505 | | |
506 | | void |
507 | | HTMLEditor::AddPositioningOffset(int32_t& aX, |
508 | | int32_t& aY) |
509 | 0 | { |
510 | 0 | // Get the positioning offset |
511 | 0 | int32_t positioningOffset = |
512 | 0 | Preferences::GetInt("editor.positioning.offset", 0); |
513 | 0 |
|
514 | 0 | aX += positioningOffset; |
515 | 0 | aY += positioningOffset; |
516 | 0 | } |
517 | | |
518 | | nsresult |
519 | | HTMLEditor::SetPositionToAbsoluteOrStatic(Element& aElement, |
520 | | bool aEnabled) |
521 | 0 | { |
522 | 0 | nsAutoString positionStr; |
523 | 0 | CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::position, |
524 | 0 | positionStr); |
525 | 0 | bool isPositioned = (positionStr.EqualsLiteral("absolute")); |
526 | 0 |
|
527 | 0 | // nothing to do if the element is already in the state we want |
528 | 0 | if (isPositioned == aEnabled) { |
529 | 0 | return NS_OK; |
530 | 0 | } |
531 | 0 | |
532 | 0 | if (aEnabled) { |
533 | 0 | return SetPositionToAbsolute(aElement); |
534 | 0 | } |
535 | 0 | |
536 | 0 | return SetPositionToStatic(aElement); |
537 | 0 | } |
538 | | |
539 | | nsresult |
540 | | HTMLEditor::SetPositionToAbsolute(Element& aElement) |
541 | 0 | { |
542 | 0 | AutoPlaceholderBatch batchIt(this); |
543 | 0 |
|
544 | 0 | int32_t x, y; |
545 | 0 | GetElementOrigin(aElement, x, y); |
546 | 0 |
|
547 | 0 | mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::position, |
548 | 0 | NS_LITERAL_STRING("absolute")); |
549 | 0 |
|
550 | 0 | AddPositioningOffset(x, y); |
551 | 0 | SnapToGrid(x, y); |
552 | 0 | SetTopAndLeft(aElement, x, y); |
553 | 0 |
|
554 | 0 | // we may need to create a br if the positioned element is alone in its |
555 | 0 | // container |
556 | 0 | nsINode* parentNode = aElement.GetParentNode(); |
557 | 0 | if (parentNode->GetChildCount() == 1) { |
558 | 0 | RefPtr<Selection> selection = GetSelection(); |
559 | 0 | if (NS_WARN_IF(!selection)) { |
560 | 0 | return NS_ERROR_FAILURE; |
561 | 0 | } |
562 | 0 | RefPtr<Element> newBrElement = |
563 | 0 | InsertBrElementWithTransaction(*selection, |
564 | 0 | EditorRawDOMPoint(parentNode, 0)); |
565 | 0 | if (NS_WARN_IF(!newBrElement)) { |
566 | 0 | return NS_ERROR_FAILURE; |
567 | 0 | } |
568 | 0 | } |
569 | 0 | return NS_OK; |
570 | 0 | } |
571 | | |
572 | | nsresult |
573 | | HTMLEditor::SetPositionToStatic(Element& aElement) |
574 | 0 | { |
575 | 0 | AutoPlaceholderBatch batchIt(this); |
576 | 0 |
|
577 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::position, |
578 | 0 | EmptyString()); |
579 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::top, |
580 | 0 | EmptyString()); |
581 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::left, |
582 | 0 | EmptyString()); |
583 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::z_index, |
584 | 0 | EmptyString()); |
585 | 0 |
|
586 | 0 | if (!HTMLEditUtils::IsImage(&aElement)) { |
587 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::width, |
588 | 0 | EmptyString()); |
589 | 0 | mCSSEditUtils->RemoveCSSProperty(aElement, *nsGkAtoms::height, |
590 | 0 | EmptyString()); |
591 | 0 | } |
592 | 0 |
|
593 | 0 | if (aElement.IsHTMLElement(nsGkAtoms::div) && |
594 | 0 | !HasStyleOrIdOrClass(&aElement)) { |
595 | 0 | RefPtr<HTMLEditRules> htmlRules = |
596 | 0 | static_cast<HTMLEditRules*>(mRules.get()); |
597 | 0 | NS_ENSURE_TRUE(htmlRules, NS_ERROR_FAILURE); |
598 | 0 | nsresult rv = htmlRules->MakeSureElemStartsAndEndsOnCR(aElement); |
599 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
600 | 0 | rv = RemoveContainerWithTransaction(aElement); |
601 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
602 | 0 | } |
603 | 0 | return NS_OK; |
604 | 0 | } |
605 | | |
606 | | NS_IMETHODIMP |
607 | | HTMLEditor::SetSnapToGridEnabled(bool aEnabled) |
608 | 0 | { |
609 | 0 | mSnapToGridEnabled = aEnabled; |
610 | 0 | return NS_OK; |
611 | 0 | } |
612 | | |
613 | | NS_IMETHODIMP |
614 | | HTMLEditor::GetSnapToGridEnabled(bool* aIsEnabled) |
615 | 0 | { |
616 | 0 | *aIsEnabled = mSnapToGridEnabled; |
617 | 0 | return NS_OK; |
618 | 0 | } |
619 | | |
620 | | NS_IMETHODIMP |
621 | | HTMLEditor::SetGridSize(uint32_t aSize) |
622 | 0 | { |
623 | 0 | mGridSize = aSize; |
624 | 0 | return NS_OK; |
625 | 0 | } |
626 | | |
627 | | NS_IMETHODIMP |
628 | | HTMLEditor::GetGridSize(uint32_t* aSize) |
629 | 0 | { |
630 | 0 | *aSize = mGridSize; |
631 | 0 | return NS_OK; |
632 | 0 | } |
633 | | |
634 | | // self-explanatory |
635 | | void |
636 | | HTMLEditor::SetTopAndLeft(Element& aElement, |
637 | | int32_t aX, |
638 | | int32_t aY) |
639 | 0 | { |
640 | 0 | AutoPlaceholderBatch batchIt(this); |
641 | 0 | mCSSEditUtils->SetCSSPropertyPixels(aElement, *nsGkAtoms::left, aX); |
642 | 0 | mCSSEditUtils->SetCSSPropertyPixels(aElement, *nsGkAtoms::top, aY); |
643 | 0 | } |
644 | | |
645 | | nsresult |
646 | | HTMLEditor::GetTemporaryStyleForFocusedPositionedElement(Element& aElement, |
647 | | nsAString& aReturn) |
648 | 0 | { |
649 | 0 | // we are going to outline the positioned element and bring it to the |
650 | 0 | // front to overlap any other element intersecting with it. But |
651 | 0 | // first, let's see what's the background and foreground colors of the |
652 | 0 | // positioned element. |
653 | 0 | // if background-image computed value is 'none, |
654 | 0 | // If the background color is 'auto' and R G B values of the foreground are |
655 | 0 | // each above #d0, use a black background |
656 | 0 | // If the background color is 'auto' and at least one of R G B values of |
657 | 0 | // the foreground is below #d0, use a white background |
658 | 0 | // Otherwise don't change background/foreground |
659 | 0 | aReturn.Truncate(); |
660 | 0 |
|
661 | 0 | nsAutoString bgImageStr; |
662 | 0 | nsresult rv = |
663 | 0 | CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::background_image, |
664 | 0 | bgImageStr); |
665 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
666 | 0 | if (!bgImageStr.EqualsLiteral("none")) { |
667 | 0 | return NS_OK; |
668 | 0 | } |
669 | 0 | |
670 | 0 | nsAutoString bgColorStr; |
671 | 0 | rv = |
672 | 0 | CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::backgroundColor, |
673 | 0 | bgColorStr); |
674 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
675 | 0 | if (!bgColorStr.EqualsLiteral("rgba(0, 0, 0, 0)")) { |
676 | 0 | return NS_OK; |
677 | 0 | } |
678 | 0 | |
679 | 0 | RefPtr<ComputedStyle> style = |
680 | 0 | nsComputedDOMStyle::GetComputedStyle(&aElement, nullptr); |
681 | 0 | NS_ENSURE_STATE(style); |
682 | 0 |
|
683 | 0 | const uint8_t kBlackBgTrigger = 0xd0; |
684 | 0 |
|
685 | 0 | nscolor color = style->StyleColor()->mColor; |
686 | 0 | if (NS_GET_R(color) >= kBlackBgTrigger && |
687 | 0 | NS_GET_G(color) >= kBlackBgTrigger && |
688 | 0 | NS_GET_B(color) >= kBlackBgTrigger) { |
689 | 0 | aReturn.AssignLiteral("black"); |
690 | 0 | } else { |
691 | 0 | aReturn.AssignLiteral("white"); |
692 | 0 | } |
693 | 0 |
|
694 | 0 | return NS_OK; |
695 | 0 | } |
696 | | |
697 | | } // namespace mozilla |