/src/mozilla-central/editor/libeditor/HTMLAnonymousNodeEditor.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 "mozilla/Attributes.h" |
8 | | #include "mozilla/dom/Element.h" |
9 | | #include "mozilla/dom/EventTarget.h" |
10 | | #include "mozilla/mozalloc.h" |
11 | | #include "nsAString.h" |
12 | | #include "nsCOMPtr.h" |
13 | | #include "nsComputedDOMStyle.h" |
14 | | #include "nsDebug.h" |
15 | | #include "nsError.h" |
16 | | #include "nsGenericHTMLElement.h" |
17 | | #include "nsGkAtoms.h" |
18 | | #include "nsAtom.h" |
19 | | #include "nsIContent.h" |
20 | | #include "nsID.h" |
21 | | #include "nsIDOMWindow.h" |
22 | | #include "nsIDocument.h" |
23 | | #include "nsIDocumentObserver.h" |
24 | | #include "nsIHTMLAbsPosEditor.h" |
25 | | #include "nsIHTMLInlineTableEditor.h" |
26 | | #include "nsIHTMLObjectResizer.h" |
27 | | #include "nsStubMutationObserver.h" |
28 | | #include "nsINode.h" |
29 | | #include "nsIPresShell.h" |
30 | | #include "nsISupportsImpl.h" |
31 | | #include "nsISupportsUtils.h" |
32 | | #include "nsLiteralString.h" |
33 | | #include "nsPresContext.h" |
34 | | #include "nsReadableUtils.h" |
35 | | #include "nsString.h" |
36 | | #include "nsStringFwd.h" |
37 | | #include "nsUnicharUtils.h" |
38 | | #include "nscore.h" |
39 | | #include "nsContentUtils.h" // for nsAutoScriptBlocker |
40 | | #include "nsROCSSPrimitiveValue.h" |
41 | | |
42 | | class nsIDOMEventListener; |
43 | | |
44 | | namespace mozilla { |
45 | | |
46 | | using namespace dom; |
47 | | |
48 | | // Retrieve the rounded number of CSS pixels from a computed CSS property. |
49 | | // |
50 | | // Note that this should only be called for properties whose resolved value |
51 | | // is CSS pixels (like width, height, left, top, right, bottom, margin, padding, |
52 | | // border-*-width, ...). |
53 | | // |
54 | | // See: https://drafts.csswg.org/cssom/#resolved-values |
55 | | static int32_t |
56 | | GetCSSFloatValue(nsComputedDOMStyle* aComputedStyle, |
57 | | const nsAString& aProperty) |
58 | 0 | { |
59 | 0 | MOZ_ASSERT(aComputedStyle); |
60 | 0 |
|
61 | 0 | // get the computed CSSValue of the property |
62 | 0 | nsAutoString value; |
63 | 0 | nsresult rv = aComputedStyle->GetPropertyValue(aProperty, value); |
64 | 0 | if (NS_FAILED(rv)) { |
65 | 0 | return 0; |
66 | 0 | } |
67 | 0 | |
68 | 0 | // We only care about resolved values, not a big deal if the element is |
69 | 0 | // undisplayed, for example, and the value is "auto" or what not. |
70 | 0 | int32_t val = value.ToInteger(&rv); |
71 | 0 | return NS_SUCCEEDED(rv) ? val : 0; |
72 | 0 | } |
73 | | |
74 | | class ElementDeletionObserver final : public nsStubMutationObserver |
75 | | { |
76 | | public: |
77 | | ElementDeletionObserver(nsIContent* aNativeAnonNode, |
78 | | nsIContent* aObservedNode) |
79 | | : mNativeAnonNode(aNativeAnonNode) |
80 | | , mObservedNode(aObservedNode) |
81 | 0 | {} |
82 | | |
83 | | NS_DECL_ISUPPORTS |
84 | | NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED |
85 | | NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED |
86 | | |
87 | | protected: |
88 | 0 | ~ElementDeletionObserver() {} |
89 | | nsIContent* mNativeAnonNode; |
90 | | nsIContent* mObservedNode; |
91 | | }; |
92 | | |
93 | | NS_IMPL_ISUPPORTS(ElementDeletionObserver, nsIMutationObserver) |
94 | | |
95 | | void |
96 | | ElementDeletionObserver::ParentChainChanged(nsIContent* aContent) |
97 | 0 | { |
98 | 0 | // If the native anonymous content has been unbound already in |
99 | 0 | // DeleteRefToAnonymousNode, mNativeAnonNode's parentNode is null. |
100 | 0 | if (aContent == mObservedNode && mNativeAnonNode && |
101 | 0 | mNativeAnonNode->GetParentNode() == aContent) { |
102 | 0 | // If the observed node has been moved to another document, there isn't much |
103 | 0 | // we can do easily. But at least be safe and unbind the native anonymous |
104 | 0 | // content and stop observing changes. |
105 | 0 | if (mNativeAnonNode->OwnerDoc() != mObservedNode->OwnerDoc()) { |
106 | 0 | mObservedNode->RemoveMutationObserver(this); |
107 | 0 | mObservedNode = nullptr; |
108 | 0 | mNativeAnonNode->RemoveMutationObserver(this); |
109 | 0 | mNativeAnonNode->UnbindFromTree(); |
110 | 0 | mNativeAnonNode = nullptr; |
111 | 0 | NS_RELEASE_THIS(); |
112 | 0 | return; |
113 | 0 | } |
114 | 0 |
|
115 | 0 | // We're staying in the same document, just rebind the native anonymous |
116 | 0 | // node so that the subtree root points to the right object etc. |
117 | 0 | mNativeAnonNode->UnbindFromTree(); |
118 | 0 | mNativeAnonNode->BindToTree(mObservedNode->GetUncomposedDoc(), |
119 | 0 | mObservedNode, |
120 | 0 | mObservedNode); |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | void |
125 | | ElementDeletionObserver::NodeWillBeDestroyed(const nsINode* aNode) |
126 | 0 | { |
127 | 0 | NS_ASSERTION(aNode == mNativeAnonNode || aNode == mObservedNode, |
128 | 0 | "Wrong aNode!"); |
129 | 0 | if (aNode == mNativeAnonNode) { |
130 | 0 | mObservedNode->RemoveMutationObserver(this); |
131 | 0 | mObservedNode = nullptr; |
132 | 0 | } else { |
133 | 0 | mNativeAnonNode->RemoveMutationObserver(this); |
134 | 0 | mNativeAnonNode->UnbindFromTree(); |
135 | 0 | mNativeAnonNode = nullptr; |
136 | 0 | } |
137 | 0 |
|
138 | 0 | NS_RELEASE_THIS(); |
139 | 0 | } |
140 | | |
141 | | ManualNACPtr |
142 | | HTMLEditor::CreateAnonymousElement(nsAtom* aTag, |
143 | | nsIContent& aParentContent, |
144 | | const nsAString& aAnonClass, |
145 | | bool aIsCreatedHidden) |
146 | 0 | { |
147 | 0 | // Don't put anonymous editor element into non-HTML element. |
148 | 0 | // It is mainly for avoiding other anonymous element being inserted |
149 | 0 | // into <svg:use>, but in general we probably don't want to insert |
150 | 0 | // some random HTML anonymous element into a non-HTML element. |
151 | 0 | if (!aParentContent.IsHTMLElement()) { |
152 | 0 | return nullptr; |
153 | 0 | } |
154 | 0 | |
155 | 0 | nsCOMPtr<nsIDocument> doc = GetDocument(); |
156 | 0 | if (NS_WARN_IF(!doc)) { |
157 | 0 | return nullptr; |
158 | 0 | } |
159 | 0 | |
160 | 0 | // Get the pres shell |
161 | 0 | nsCOMPtr<nsIPresShell> ps = GetPresShell(); |
162 | 0 | if (NS_WARN_IF(!ps)) { |
163 | 0 | return nullptr; |
164 | 0 | } |
165 | 0 | |
166 | 0 | // Create a new node through the element factory |
167 | 0 | RefPtr<Element> newContentRaw = CreateHTMLContent(aTag); |
168 | 0 | if (NS_WARN_IF(!newContentRaw)) { |
169 | 0 | return nullptr; |
170 | 0 | } |
171 | 0 | |
172 | 0 | // add the "hidden" class if needed |
173 | 0 | if (aIsCreatedHidden) { |
174 | 0 | nsresult rv = |
175 | 0 | newContentRaw->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, |
176 | 0 | NS_LITERAL_STRING("hidden"), true); |
177 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
178 | 0 | return nullptr; |
179 | 0 | } |
180 | 0 | } |
181 | 0 | |
182 | 0 | // add an _moz_anonclass attribute if needed |
183 | 0 | if (!aAnonClass.IsEmpty()) { |
184 | 0 | nsresult rv = |
185 | 0 | newContentRaw->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_anonclass, |
186 | 0 | aAnonClass, true); |
187 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
188 | 0 | return nullptr; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | |
192 | 0 | { |
193 | 0 | nsAutoScriptBlocker scriptBlocker; |
194 | 0 |
|
195 | 0 | // establish parenthood of the element |
196 | 0 | newContentRaw->SetIsNativeAnonymousRoot(); |
197 | 0 | nsresult rv = |
198 | 0 | newContentRaw->BindToTree(doc, &aParentContent, &aParentContent); |
199 | 0 | if (NS_FAILED(rv)) { |
200 | 0 | newContentRaw->UnbindFromTree(); |
201 | 0 | return nullptr; |
202 | 0 | } |
203 | 0 | } |
204 | 0 | |
205 | 0 | ManualNACPtr newContent(newContentRaw.forget()); |
206 | 0 |
|
207 | 0 | // Must style the new element, otherwise the PostRecreateFramesFor call |
208 | 0 | // below will do nothing. |
209 | 0 | ServoStyleSet* styleSet = ps->StyleSet(); |
210 | 0 | // Sometimes editor likes to append anonymous content to elements |
211 | 0 | // in display:none subtrees, so avoid styling in those cases. |
212 | 0 | if (ServoStyleSet::MayTraverseFrom(newContent)) { |
213 | 0 | styleSet->StyleNewSubtree(newContent); |
214 | 0 | } |
215 | 0 |
|
216 | 0 | ElementDeletionObserver* observer = |
217 | 0 | new ElementDeletionObserver(newContent, &aParentContent); |
218 | 0 | NS_ADDREF(observer); // NodeWillBeDestroyed releases. |
219 | 0 | aParentContent.AddMutationObserver(observer); |
220 | 0 | newContent->AddMutationObserver(observer); |
221 | 0 |
|
222 | | #ifdef DEBUG |
223 | | // Editor anonymous content gets passed to PostRecreateFramesFor... which |
224 | | // can't _really_ deal with anonymous content (because it can't get the frame |
225 | | // tree ordering right). But for us the ordering doesn't matter so this is |
226 | | // sort of ok. |
227 | | newContent->SetProperty(nsGkAtoms::restylableAnonymousNode, |
228 | | reinterpret_cast<void*>(true)); |
229 | | #endif // DEBUG |
230 | |
|
231 | 0 | // display the element |
232 | 0 | ps->PostRecreateFramesFor(newContent); |
233 | 0 |
|
234 | 0 | return newContent; |
235 | 0 | } |
236 | | |
237 | | // Removes event listener and calls DeleteRefToAnonymousNode. |
238 | | void |
239 | | HTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent, |
240 | | nsIDOMEventListener* aListener, |
241 | | bool aUseCapture, |
242 | | ManualNACPtr aElement, |
243 | | nsIPresShell* aShell) |
244 | 0 | { |
245 | 0 | if (aElement) { |
246 | 0 | aElement->RemoveEventListener(aEvent, aListener, aUseCapture); |
247 | 0 | } |
248 | 0 | DeleteRefToAnonymousNode(std::move(aElement), aShell); |
249 | 0 | } |
250 | | |
251 | | // Deletes all references to an anonymous element |
252 | | void |
253 | | HTMLEditor::DeleteRefToAnonymousNode(ManualNACPtr aContent, |
254 | | nsIPresShell* aShell) |
255 | 0 | { |
256 | 0 | // call ContentRemoved() for the anonymous content |
257 | 0 | // node so its references get removed from the frame manager's |
258 | 0 | // undisplay map, and its layout frames get destroyed! |
259 | 0 |
|
260 | 0 | if (NS_WARN_IF(!aContent)) { |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | |
264 | 0 | nsIContent* parentContent = aContent->GetParent(); |
265 | 0 | if (NS_WARN_IF(!parentContent)) { |
266 | 0 | // aContent was already removed? |
267 | 0 | return; |
268 | 0 | } |
269 | 0 | |
270 | 0 | nsAutoScriptBlocker scriptBlocker; |
271 | 0 | // Need to check whether aShell has been destroyed (but not yet deleted). |
272 | 0 | // See bug 338129. |
273 | 0 | if (aContent->IsInComposedDoc() && aShell && !aShell->IsDestroying()) { |
274 | 0 | MOZ_ASSERT(aContent->IsRootOfAnonymousSubtree()); |
275 | 0 | MOZ_ASSERT(!aContent->GetPreviousSibling(), "NAC has no siblings"); |
276 | 0 |
|
277 | 0 | // FIXME(emilio): This is the only caller to PresShell::ContentRemoved that |
278 | 0 | // passes NAC into it. This is not great! |
279 | 0 | aShell->ContentRemoved(aContent, nullptr); |
280 | 0 | } |
281 | 0 |
|
282 | 0 | // The ManualNACPtr destructor will invoke UnbindFromTree. |
283 | 0 | } |
284 | | |
285 | | void |
286 | | HTMLEditor::HideAnonymousEditingUIs() |
287 | 0 | { |
288 | 0 | if (mAbsolutelyPositionedObject) { |
289 | 0 | HideGrabberInternal(); |
290 | 0 | NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed"); |
291 | 0 | } |
292 | 0 | if (mInlineEditedCell) { |
293 | 0 | HideInlineTableEditingUIInternal(); |
294 | 0 | NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed"); |
295 | 0 | } |
296 | 0 | if (mResizedObject) { |
297 | 0 | DebugOnly<nsresult> rv = HideResizersInternal(); |
298 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HideResizersInternal() failed"); |
299 | 0 | NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed"); |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | | void |
304 | | HTMLEditor::HideAnonymousEditingUIsIfUnnecessary() |
305 | 0 | { |
306 | 0 | // XXX Perhaps, this is wrong approach to hide multiple UIs because |
307 | 0 | // hiding one UI may causes overwriting existing UI with newly |
308 | 0 | // created one. In such case, we will leak ovewritten UI. |
309 | 0 | if (!IsAbsolutePositionEditorEnabled() && mAbsolutelyPositionedObject) { |
310 | 0 | // XXX If we're moving something, we need to cancel or commit the |
311 | 0 | // operation now. |
312 | 0 | HideGrabberInternal(); |
313 | 0 | NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed"); |
314 | 0 | } |
315 | 0 | if (!IsInlineTableEditorEnabled() && mInlineEditedCell) { |
316 | 0 | // XXX If we're resizing a table element, we need to cancel or commit the |
317 | 0 | // operation now. |
318 | 0 | HideInlineTableEditingUIInternal(); |
319 | 0 | NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed"); |
320 | 0 | } |
321 | 0 | if (!IsObjectResizerEnabled() && mResizedObject) { |
322 | 0 | // XXX If we're resizing something, we need to cancel or commit the |
323 | 0 | // operation now. |
324 | 0 | DebugOnly<nsresult> rv = HideResizersInternal(); |
325 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HideResizersInternal() failed"); |
326 | 0 | NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed"); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | NS_IMETHODIMP |
331 | | HTMLEditor::CheckSelectionStateForAnonymousButtons(Selection* aSelection) |
332 | 0 | { |
333 | 0 | if (NS_WARN_IF(!aSelection)) { |
334 | 0 | return NS_ERROR_INVALID_ARG; |
335 | 0 | } |
336 | 0 | nsresult rv = RefereshEditingUI(*aSelection); |
337 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
338 | 0 | return rv; |
339 | 0 | } |
340 | 0 | return NS_OK; |
341 | 0 | } |
342 | | |
343 | | nsresult |
344 | | HTMLEditor::RefereshEditingUI(Selection& aSelection) |
345 | 0 | { |
346 | 0 | // First, we need to remove unnecessary editing UI now since some of them |
347 | 0 | // may be disabled while them are visible. |
348 | 0 | HideAnonymousEditingUIsIfUnnecessary(); |
349 | 0 |
|
350 | 0 | // early way out if all contextual UI extensions are disabled |
351 | 0 | if (!IsObjectResizerEnabled() && |
352 | 0 | !IsAbsolutePositionEditorEnabled() && |
353 | 0 | !IsInlineTableEditorEnabled()) { |
354 | 0 | return NS_OK; |
355 | 0 | } |
356 | 0 | |
357 | 0 | // Don't change selection state if we're moving. |
358 | 0 | if (mIsMoving) { |
359 | 0 | return NS_OK; |
360 | 0 | } |
361 | 0 | |
362 | 0 | // let's get the containing element of the selection |
363 | 0 | RefPtr<Element> focusElement = GetSelectionContainerElement(aSelection); |
364 | 0 | if (NS_WARN_IF(!focusElement)) { |
365 | 0 | return NS_OK; |
366 | 0 | } |
367 | 0 | |
368 | 0 | // If we're not in a document, don't try to add resizers |
369 | 0 | if (!focusElement->IsInUncomposedDoc()) { |
370 | 0 | return NS_OK; |
371 | 0 | } |
372 | 0 | |
373 | 0 | // what's its tag? |
374 | 0 | nsAtom* focusTagAtom = focusElement->NodeInfo()->NameAtom(); |
375 | 0 |
|
376 | 0 | RefPtr<Element> absPosElement; |
377 | 0 | if (IsAbsolutePositionEditorEnabled()) { |
378 | 0 | // Absolute Positioning support is enabled, is the selection contained |
379 | 0 | // in an absolutely positioned element ? |
380 | 0 | absPosElement = GetAbsolutelyPositionedSelectionContainer(); |
381 | 0 | } |
382 | 0 |
|
383 | 0 | RefPtr<Element> cellElement; |
384 | 0 | if (IsObjectResizerEnabled() || IsInlineTableEditorEnabled()) { |
385 | 0 | // Resizing or Inline Table Editing is enabled, we need to check if the |
386 | 0 | // selection is contained in a table cell |
387 | 0 | cellElement = |
388 | 0 | GetElementOrParentByTagNameAtSelection(aSelection, *nsGkAtoms::td); |
389 | 0 | } |
390 | 0 |
|
391 | 0 | if (IsObjectResizerEnabled() && cellElement) { |
392 | 0 | // we are here because Resizing is enabled AND selection is contained in |
393 | 0 | // a cell |
394 | 0 |
|
395 | 0 | // get the enclosing table |
396 | 0 | if (nsGkAtoms::img != focusTagAtom) { |
397 | 0 | // the element container of the selection is not an image, so we'll show |
398 | 0 | // the resizers around the table |
399 | 0 | // XXX There may be a bug. cellElement may be not in <table> in invalid |
400 | 0 | // tree. So, perhaps, GetEnclosingTable() returns nullptr, we should |
401 | 0 | // not set focusTagAtom to nsGkAtoms::table. |
402 | 0 | focusElement = GetEnclosingTable(cellElement); |
403 | 0 | focusTagAtom = nsGkAtoms::table; |
404 | 0 | } |
405 | 0 | } |
406 | 0 |
|
407 | 0 | // we allow resizers only around images, tables, and absolutely positioned |
408 | 0 | // elements. If we don't have image/table, let's look at the latter case. |
409 | 0 | if (nsGkAtoms::img != focusTagAtom && nsGkAtoms::table != focusTagAtom) { |
410 | 0 | focusElement = absPosElement; |
411 | 0 | } |
412 | 0 |
|
413 | 0 | // at this point, focusElement contains the element for Resizing, |
414 | 0 | // cellElement contains the element for InlineTableEditing |
415 | 0 | // absPosElement contains the element for Positioning |
416 | 0 |
|
417 | 0 | // Note: All the Hide/Show methods below may change attributes on real |
418 | 0 | // content which means a DOMAttrModified handler may cause arbitrary |
419 | 0 | // side effects while this code runs (bug 420439). |
420 | 0 |
|
421 | 0 | if (IsAbsolutePositionEditorEnabled() && mAbsolutelyPositionedObject && |
422 | 0 | absPosElement != mAbsolutelyPositionedObject) { |
423 | 0 | HideGrabberInternal(); |
424 | 0 | NS_ASSERTION(!mAbsolutelyPositionedObject, "HideGrabber failed"); |
425 | 0 | } |
426 | 0 |
|
427 | 0 | if (IsObjectResizerEnabled() && mResizedObject && |
428 | 0 | mResizedObject != focusElement) { |
429 | 0 | // Perhaps, even if HideResizersInternal() failed, we should try to hide |
430 | 0 | // inline table editing UI. However, it returns error only when we cannot |
431 | 0 | // do anything. So, it's okay for now. |
432 | 0 | nsresult rv = HideResizersInternal(); |
433 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
434 | 0 | return rv; |
435 | 0 | } |
436 | 0 | NS_ASSERTION(!mResizedObject, "HideResizersInternal() failed"); |
437 | 0 | } |
438 | 0 |
|
439 | 0 | if (IsInlineTableEditorEnabled() && mInlineEditedCell && |
440 | 0 | mInlineEditedCell != cellElement) { |
441 | 0 | HideInlineTableEditingUIInternal(); |
442 | 0 | NS_ASSERTION(!mInlineEditedCell, "HideInlineTableEditingUIInternal failed"); |
443 | 0 | } |
444 | 0 |
|
445 | 0 | // now, let's display all contextual UI for good |
446 | 0 | nsIContent* hostContent = GetActiveEditingHost(); |
447 | 0 |
|
448 | 0 | if (IsObjectResizerEnabled() && focusElement && |
449 | 0 | IsModifiableNode(*focusElement) && focusElement != hostContent) { |
450 | 0 | if (nsGkAtoms::img == focusTagAtom) { |
451 | 0 | mResizedObjectIsAnImage = true; |
452 | 0 | } |
453 | 0 | if (mResizedObject) { |
454 | 0 | nsresult rv = RefreshResizersInternal(); |
455 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
456 | 0 | return rv; |
457 | 0 | } |
458 | 0 | } else { |
459 | 0 | nsresult rv = ShowResizersInternal(*focusElement); |
460 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
461 | 0 | return rv; |
462 | 0 | } |
463 | 0 | } |
464 | 0 | } |
465 | 0 | |
466 | 0 | if (IsAbsolutePositionEditorEnabled() && absPosElement && |
467 | 0 | IsModifiableNode(*absPosElement) && absPosElement != hostContent) { |
468 | 0 | if (mAbsolutelyPositionedObject) { |
469 | 0 | nsresult rv = RefreshGrabberInternal(); |
470 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
471 | 0 | return rv; |
472 | 0 | } |
473 | 0 | } else { |
474 | 0 | nsresult rv = ShowGrabberInternal(*absPosElement); |
475 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
476 | 0 | return rv; |
477 | 0 | } |
478 | 0 | } |
479 | 0 | } |
480 | 0 | |
481 | 0 | if (IsInlineTableEditorEnabled() && cellElement && |
482 | 0 | IsModifiableNode(*cellElement) && cellElement != hostContent) { |
483 | 0 | if (mInlineEditedCell) { |
484 | 0 | nsresult rv = RefreshInlineTableEditingUIInternal(); |
485 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
486 | 0 | return rv; |
487 | 0 | } |
488 | 0 | } else { |
489 | 0 | nsresult rv = ShowInlineTableEditingUIInternal(*cellElement); |
490 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
491 | 0 | return rv; |
492 | 0 | } |
493 | 0 | } |
494 | 0 | } |
495 | 0 | |
496 | 0 | return NS_OK; |
497 | 0 | } |
498 | | |
499 | | // Resizing and Absolute Positioning need to know everything about the |
500 | | // containing box of the element: position, size, margins, borders |
501 | | nsresult |
502 | | HTMLEditor::GetPositionAndDimensions(Element& aElement, |
503 | | int32_t& aX, |
504 | | int32_t& aY, |
505 | | int32_t& aW, |
506 | | int32_t& aH, |
507 | | int32_t& aBorderLeft, |
508 | | int32_t& aBorderTop, |
509 | | int32_t& aMarginLeft, |
510 | | int32_t& aMarginTop) |
511 | 0 | { |
512 | 0 | // Is the element positioned ? let's check the cheap way first... |
513 | 0 | bool isPositioned = |
514 | 0 | aElement.HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_abspos); |
515 | 0 | if (!isPositioned) { |
516 | 0 | // hmmm... the expensive way now... |
517 | 0 | nsAutoString positionStr; |
518 | 0 | CSSEditUtils::GetComputedProperty(aElement, *nsGkAtoms::position, |
519 | 0 | positionStr); |
520 | 0 | isPositioned = positionStr.EqualsLiteral("absolute"); |
521 | 0 | } |
522 | 0 |
|
523 | 0 | if (isPositioned) { |
524 | 0 | // Yes, it is absolutely positioned |
525 | 0 | mResizedObjectIsAbsolutelyPositioned = true; |
526 | 0 |
|
527 | 0 | // Get the all the computed css styles attached to the element node |
528 | 0 | RefPtr<nsComputedDOMStyle> cssDecl = |
529 | 0 | CSSEditUtils::GetComputedStyle(&aElement); |
530 | 0 | NS_ENSURE_STATE(cssDecl); |
531 | 0 |
|
532 | 0 | aBorderLeft = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("border-left-width")); |
533 | 0 | aBorderTop = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("border-top-width")); |
534 | 0 | aMarginLeft = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("margin-left")); |
535 | 0 | aMarginTop = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("margin-top")); |
536 | 0 |
|
537 | 0 | aX = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("left")) + |
538 | 0 | aMarginLeft + aBorderLeft; |
539 | 0 | aY = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("top")) + |
540 | 0 | aMarginTop + aBorderTop; |
541 | 0 | aW = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("width")); |
542 | 0 | aH = GetCSSFloatValue(cssDecl, NS_LITERAL_STRING("height")); |
543 | 0 | } else { |
544 | 0 | mResizedObjectIsAbsolutelyPositioned = false; |
545 | 0 | RefPtr<nsGenericHTMLElement> htmlElement = |
546 | 0 | nsGenericHTMLElement::FromNode(aElement); |
547 | 0 | if (!htmlElement) { |
548 | 0 | return NS_ERROR_NULL_POINTER; |
549 | 0 | } |
550 | 0 | GetElementOrigin(aElement, aX, aY); |
551 | 0 |
|
552 | 0 | aW = htmlElement->OffsetWidth(); |
553 | 0 | aH = htmlElement->OffsetHeight(); |
554 | 0 |
|
555 | 0 | aBorderLeft = 0; |
556 | 0 | aBorderTop = 0; |
557 | 0 | aMarginLeft = 0; |
558 | 0 | aMarginTop = 0; |
559 | 0 | } |
560 | 0 | return NS_OK; |
561 | 0 | } |
562 | | |
563 | | // self-explanatory |
564 | | void |
565 | | HTMLEditor::SetAnonymousElementPosition(int32_t aX, |
566 | | int32_t aY, |
567 | | Element* aElement) |
568 | 0 | { |
569 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*aElement, *nsGkAtoms::left, aX); |
570 | 0 | mCSSEditUtils->SetCSSPropertyPixels(*aElement, *nsGkAtoms::top, aY); |
571 | 0 | } |
572 | | |
573 | | } // namespace mozilla |